rspec-core 2.10.1 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +35 -2
- data/README.md +14 -13
- data/features/command_line/example_name_option.feature +15 -0
- data/features/helper_methods/modules.feature +3 -3
- data/lib/rspec/core.rb +6 -2
- data/lib/rspec/core/configuration.rb +61 -26
- data/lib/rspec/core/configuration_options.rb +5 -1
- data/lib/rspec/core/deprecation.rb +0 -15
- data/lib/rspec/core/drb_command_line.rb +3 -0
- data/lib/rspec/core/drb_options.rb +3 -1
- data/lib/rspec/core/dsl.rb +4 -2
- data/lib/rspec/core/example.rb +85 -23
- data/lib/rspec/core/example_group.rb +103 -78
- data/lib/rspec/core/hooks.rb +68 -33
- data/lib/rspec/core/let.rb +0 -1
- data/lib/rspec/core/mocking/with_mocha.rb +10 -4
- data/lib/rspec/core/option_parser.rb +3 -2
- data/lib/rspec/core/project_initializer.rb +7 -1
- data/lib/rspec/core/runner.rb +2 -2
- data/lib/rspec/core/shared_context.rb +2 -2
- data/lib/rspec/core/shared_example_group.rb +38 -14
- data/lib/rspec/core/subject.rb +67 -52
- data/lib/rspec/core/version.rb +1 -1
- data/spec/rspec/core/command_line_spec.rb +68 -126
- data/spec/rspec/core/configuration_options_spec.rb +20 -4
- data/spec/rspec/core/configuration_spec.rb +61 -21
- data/spec/rspec/core/drb_command_line_spec.rb +1 -0
- data/spec/rspec/core/drb_options_spec.rb +1 -0
- data/spec/rspec/core/dsl_spec.rb +17 -0
- data/spec/rspec/core/example_group_spec.rb +19 -11
- data/spec/rspec/core/example_spec.rb +34 -0
- data/spec/rspec/core/option_parser_spec.rb +2 -1
- data/spec/rspec/core/shared_example_group_spec.rb +9 -9
- data/spec/rspec/core/subject_spec.rb +14 -0
- data/spec/rspec/core_spec.rb +18 -8
- data/spec/spec_helper.rb +1 -2
- metadata +7 -5
@@ -1,7 +1,7 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Core
|
3
|
-
# ExampleGroup and Example are the main structural elements of
|
4
|
-
# Consider this example:
|
3
|
+
# ExampleGroup and {Example} are the main structural elements of
|
4
|
+
# rspec-core. Consider this example:
|
5
5
|
#
|
6
6
|
# describe Thing do
|
7
7
|
# it "does something" do
|
@@ -47,81 +47,118 @@ module RSpec
|
|
47
47
|
alias_method :display_name, :description
|
48
48
|
# @private
|
49
49
|
alias_method :describes, :described_class
|
50
|
-
end
|
51
|
-
|
52
|
-
# @private
|
53
|
-
def self.define_example_method(name, extra_options={})
|
54
|
-
module_eval(<<-END_RUBY, __FILE__, __LINE__)
|
55
|
-
def self.#{name}(desc=nil, *args, &block)
|
56
|
-
options = build_metadata_hash_from(args)
|
57
|
-
options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
|
58
|
-
options.update(#{extra_options.inspect})
|
59
|
-
examples << RSpec::Core::Example.new(self, desc, options, block)
|
60
|
-
examples.last
|
61
|
-
end
|
62
|
-
END_RUBY
|
63
|
-
end
|
64
|
-
|
65
|
-
define_example_method :example
|
66
|
-
define_example_method :it
|
67
|
-
define_example_method :specify
|
68
50
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
51
|
+
# @private
|
52
|
+
# @macro [attach] define_example_method
|
53
|
+
# @param [String] name
|
54
|
+
# @param [Hash] extra_options
|
55
|
+
# @param [Block] implementation
|
56
|
+
def self.define_example_method(name, extra_options={})
|
57
|
+
module_eval(<<-END_RUBY, __FILE__, __LINE__)
|
58
|
+
def #{name}(desc=nil, *args, &block)
|
59
|
+
options = build_metadata_hash_from(args)
|
60
|
+
options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
|
61
|
+
options.update(#{extra_options.inspect})
|
62
|
+
examples << RSpec::Core::Example.new(self, desc, options, block)
|
63
|
+
examples.last
|
64
|
+
end
|
65
|
+
END_RUBY
|
66
|
+
end
|
76
67
|
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
# Defines an example within a group.
|
69
|
+
define_example_method :example
|
70
|
+
# Defines an example within a group.
|
71
|
+
#
|
72
|
+
# @see example
|
73
|
+
define_example_method :it
|
74
|
+
# Defines an example within a group.
|
75
|
+
# This is here primarily for backward compatibility with early versions
|
76
|
+
# of RSpec which used `context` and `specify` instead of `describe` and
|
77
|
+
# `it`.
|
78
|
+
define_example_method :specify
|
79
|
+
|
80
|
+
# Shortcut to define an example with `:focus` => true
|
81
|
+
define_example_method :focus, :focused => true, :focus => true
|
82
|
+
# Shortcut to define an example with `:focus` => true
|
83
|
+
define_example_method :focused, :focused => true, :focus => true
|
84
|
+
|
85
|
+
# Shortcut to define an example with :pending => true
|
86
|
+
define_example_method :pending, :pending => true
|
87
|
+
# Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
|
88
|
+
define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
|
89
|
+
# Shortcut to define an example with :pending => 'Temporarily disabled with xit'
|
90
|
+
define_example_method :xit, :pending => 'Temporarily disabled with xit'
|
91
|
+
# Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
|
92
|
+
define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
|
93
|
+
|
94
|
+
# Works like `alias_method :name, :example` with the added benefit of
|
95
|
+
# assigning default metadata to the generated example.
|
96
|
+
#
|
97
|
+
# @note Use with caution. This extends the language used in your
|
98
|
+
# specs, but does not add any additional documentation. We use this
|
99
|
+
# in rspec to define methods like `focus` and `xit`, but we also add
|
100
|
+
# docs for those methods.
|
101
|
+
def alias_example_to name, extra={}
|
102
|
+
(class << self; self; end).define_example_method name, extra
|
103
|
+
end
|
80
104
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
105
|
+
# @private
|
106
|
+
# @macro [attach] define_nested_shared_group_method
|
107
|
+
#
|
108
|
+
# @see SharedExampleGroup
|
109
|
+
def self.define_nested_shared_group_method(new_name, report_label=nil)
|
110
|
+
module_eval(<<-END_RUBY, __FILE__, __LINE__)
|
111
|
+
def #{new_name}(name, *args, &customization_block)
|
112
|
+
group = describe("#{report_label || "it should behave like"} \#{name}") do
|
113
|
+
find_and_eval_shared("examples", name, *args, &customization_block)
|
114
|
+
end
|
115
|
+
group.metadata[:shared_group_name] = name
|
116
|
+
group
|
87
117
|
end
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
END_RUBY
|
92
|
-
end
|
93
|
-
|
94
|
-
define_nested_shared_group_method :it_should_behave_like
|
118
|
+
END_RUBY
|
119
|
+
end
|
95
120
|
|
96
|
-
|
97
|
-
|
121
|
+
# Generates a nested example group and includes the shared content
|
122
|
+
# mapped to `name` in the nested group.
|
123
|
+
define_nested_shared_group_method :it_behaves_like, "behaves like"
|
124
|
+
# Generates a nested example group and includes the shared content
|
125
|
+
# mapped to `name` in the nested group.
|
126
|
+
define_nested_shared_group_method :it_should_behave_like
|
127
|
+
|
128
|
+
# Works like `alias_method :name, :it_behaves_like` with the added
|
129
|
+
# benefit of assigning default metadata to the generated example.
|
130
|
+
#
|
131
|
+
# @note Use with caution. This extends the language used in your
|
132
|
+
# specs, but does not add any additional documentation. We use this
|
133
|
+
# in rspec to define `it_should_behave_like` (for backward
|
134
|
+
# compatibility), but we also add docs for that method.
|
135
|
+
def alias_it_behaves_like_to name, *args, &block
|
136
|
+
(class << self; self; end).define_nested_shared_group_method name, *args, &block
|
137
|
+
end
|
98
138
|
end
|
99
139
|
|
100
|
-
|
101
|
-
|
102
|
-
#
|
140
|
+
# Includes shared content mapped to `name` directly in the group in which
|
141
|
+
# it is declared, as opposed to `it_behaves_like`, which creates a nested
|
142
|
+
# group. If given a block, that block is also eval'd in the current context.
|
103
143
|
#
|
104
144
|
# @see SharedExampleGroup
|
105
|
-
def self.include_context(name, *args)
|
106
|
-
|
145
|
+
def self.include_context(name, *args, &block)
|
146
|
+
find_and_eval_shared("context", name, *args, &block)
|
107
147
|
end
|
108
148
|
|
109
|
-
# Includes shared content
|
149
|
+
# Includes shared content mapped to `name` directly in the group in which
|
150
|
+
# it is declared, as opposed to `it_behaves_like`, which creates a nested
|
151
|
+
# group. If given a block, that block is also eval'd in the current context.
|
110
152
|
#
|
111
153
|
# @see SharedExampleGroup
|
112
|
-
def self.include_examples(name, *args)
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
# @private
|
117
|
-
def self.block_not_supported(label)
|
118
|
-
warn("Customization blocks not supported for include_#{label}. Use it_behaves_like instead.")
|
154
|
+
def self.include_examples(name, *args, &block)
|
155
|
+
find_and_eval_shared("examples", name, *args, &block)
|
119
156
|
end
|
120
157
|
|
121
158
|
# @private
|
122
159
|
def self.find_and_eval_shared(label, name, *args, &customization_block)
|
123
160
|
raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
|
124
|
-
|
161
|
+
shared_block = world.shared_example_groups[name]
|
125
162
|
|
126
163
|
module_eval_with_args(*args, &shared_block)
|
127
164
|
module_eval(&customization_block) if customization_block
|
@@ -149,7 +186,7 @@ module RSpec
|
|
149
186
|
end
|
150
187
|
|
151
188
|
# @private
|
152
|
-
# @return [Metadata] belonging to the parent of a nested
|
189
|
+
# @return [Metadata] belonging to the parent of a nested {ExampleGroup}
|
153
190
|
def self.superclass_metadata
|
154
191
|
@superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
|
155
192
|
end
|
@@ -247,19 +284,7 @@ module RSpec
|
|
247
284
|
args.unshift(symbol_description) if symbol_description
|
248
285
|
@metadata = RSpec::Core::Metadata.new(superclass_metadata).process(*args)
|
249
286
|
world.configure_group(self)
|
250
|
-
|
251
|
-
RSpec.configuration.hooks[_when][:each].each do |hook|
|
252
|
-
unless ancestors.any? {|a| a.hooks[_when][:each].include? hook }
|
253
|
-
hooks[_when][:each] << hook # each's get filtered later per example
|
254
|
-
end
|
255
|
-
end
|
256
|
-
next if _when == :around # no around(:all) hooks
|
257
|
-
RSpec.configuration.hooks[_when][:all].each do |hook|
|
258
|
-
unless ancestors.any? {|a| a.hooks[_when][:all].include? hook }
|
259
|
-
hooks[_when][:all] << hook if hook.options_apply?(self)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
287
|
+
hooks.register_globals(self, RSpec.configuration.hooks)
|
263
288
|
end
|
264
289
|
|
265
290
|
# @private
|
@@ -318,7 +343,7 @@ An error occurred in an after(:all) hook.
|
|
318
343
|
#{e.class}: #{e.message}
|
319
344
|
occurred at #{e.backtrace.first}
|
320
345
|
|
321
|
-
|
346
|
+
EOS
|
322
347
|
end
|
323
348
|
end
|
324
349
|
|
@@ -401,11 +426,11 @@ An error occurred in an after(:all) hook.
|
|
401
426
|
end
|
402
427
|
|
403
428
|
# @attr_reader
|
404
|
-
# Returns the
|
429
|
+
# Returns the {Example} object that wraps this instance of
|
405
430
|
# `ExampleGroup`
|
406
431
|
attr_accessor :example
|
407
432
|
|
408
|
-
# @deprecated use
|
433
|
+
# @deprecated use {ExampleGroup#example}
|
409
434
|
def running_example
|
410
435
|
RSpec.deprecate("running_example", "example")
|
411
436
|
example
|
@@ -428,12 +453,12 @@ An error occurred in an after(:all) hook.
|
|
428
453
|
# @private
|
429
454
|
# instance_evals the block, capturing and reporting an exception if
|
430
455
|
# raised
|
431
|
-
def instance_eval_with_rescue(&hook)
|
456
|
+
def instance_eval_with_rescue(context = nil, &hook)
|
432
457
|
begin
|
433
458
|
instance_eval(&hook)
|
434
459
|
rescue Exception => e
|
435
460
|
raise unless example
|
436
|
-
example.set_exception(e)
|
461
|
+
example.set_exception(e, context)
|
437
462
|
end
|
438
463
|
end
|
439
464
|
end
|
data/lib/rspec/core/hooks.rb
CHANGED
@@ -32,7 +32,7 @@ module RSpec
|
|
32
32
|
include HookExtension
|
33
33
|
|
34
34
|
def run(example)
|
35
|
-
example.instance_eval_with_rescue(&self)
|
35
|
+
example.instance_eval_with_rescue("in an after hook", &self)
|
36
36
|
end
|
37
37
|
|
38
38
|
def display_name
|
@@ -48,7 +48,16 @@ module RSpec
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
module HookCollectionAliases
|
52
|
+
def self.included(host)
|
53
|
+
host.send :alias_method, :prepend, :unshift
|
54
|
+
host.send :alias_method, :append, :push
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
51
58
|
class HookCollection < Array
|
59
|
+
include HookCollectionAliases
|
60
|
+
|
52
61
|
def for(example_or_group)
|
53
62
|
self.class.new(select {|hook| hook.options_apply?(example_or_group)}).
|
54
63
|
with(example_or_group)
|
@@ -64,18 +73,9 @@ module RSpec
|
|
64
73
|
end
|
65
74
|
end
|
66
75
|
|
67
|
-
class GroupHookCollection < Array
|
68
|
-
def for(group)
|
69
|
-
@group = group
|
70
|
-
self
|
71
|
-
end
|
72
|
-
|
73
|
-
def run
|
74
|
-
shift.run(@group) until empty?
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
76
|
class AroundHookCollection < Array
|
77
|
+
include HookCollectionAliases
|
78
|
+
|
79
79
|
def for(example, initial_procsy=nil)
|
80
80
|
self.class.new(select {|hook| hook.options_apply?(example)}).
|
81
81
|
with(example, initial_procsy)
|
@@ -96,13 +96,43 @@ module RSpec
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
class GroupHookCollection < Array
|
100
|
+
def for(group)
|
101
|
+
@group = group
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def run
|
106
|
+
shift.run(@group) until empty?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module RegistersGlobals
|
111
|
+
def register_globals host, globals
|
112
|
+
[:before, :after, :around].each do |position|
|
113
|
+
process host, globals, position, :each
|
114
|
+
next if position == :around # no around(:all) hooks
|
115
|
+
process host, globals, position, :all
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def process host, globals, position, scope
|
121
|
+
globals[position][scope].each do |hook|
|
122
|
+
unless host.ancestors.any? { |a| a.hooks[position][scope].include? hook }
|
123
|
+
self[position][scope] << hook if scope == :each || hook.options_apply?(host)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
99
129
|
# @private
|
100
130
|
def hooks
|
101
131
|
@hooks ||= {
|
102
132
|
:around => { :each => AroundHookCollection.new },
|
103
|
-
:before => { :each =>
|
104
|
-
:after => { :each =>
|
105
|
-
}
|
133
|
+
:before => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new },
|
134
|
+
:after => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new }
|
135
|
+
}.extend(RegistersGlobals)
|
106
136
|
end
|
107
137
|
|
108
138
|
# @api public
|
@@ -126,12 +156,11 @@ module RSpec
|
|
126
156
|
#
|
127
157
|
# Declare a block of code to be run before each example (using `:each`)
|
128
158
|
# or once before any example (using `:all`). These are usually declared
|
129
|
-
# directly in the
|
130
|
-
#
|
159
|
+
# directly in the {ExampleGroup} to which they apply, but they can also
|
160
|
+
# be shared across multiple groups.
|
131
161
|
#
|
132
162
|
# You can also use `before(:suite)` to run a block of code before any
|
133
|
-
# example groups are run. This should be declared in
|
134
|
-
# [RSpec.configure](../../RSpec#configure-class_method)
|
163
|
+
# example groups are run. This should be declared in {RSpec.configure}
|
135
164
|
#
|
136
165
|
# Instance variables declared in `before(:each)` or `before(:all)` are
|
137
166
|
# accessible within each example.
|
@@ -232,7 +261,7 @@ module RSpec
|
|
232
261
|
# rspec-rails, but it will not be wrapped in a transaction for you, so
|
233
262
|
# you are on your own to clean up in an `after(:all)` block.
|
234
263
|
#
|
235
|
-
# @example before(:each) declared in an
|
264
|
+
# @example before(:each) declared in an {ExampleGroup}
|
236
265
|
#
|
237
266
|
# describe Thing do
|
238
267
|
# before(:each) do
|
@@ -244,7 +273,7 @@ module RSpec
|
|
244
273
|
# end
|
245
274
|
# end
|
246
275
|
#
|
247
|
-
# @example before(:all) declared in an
|
276
|
+
# @example before(:all) declared in an {ExampleGroup}
|
248
277
|
#
|
249
278
|
# describe Parser do
|
250
279
|
# before(:all) do
|
@@ -264,8 +293,7 @@ module RSpec
|
|
264
293
|
# end
|
265
294
|
# end
|
266
295
|
def before(*args, &block)
|
267
|
-
|
268
|
-
hooks[:before][scope] << block.extend(BeforeHookExtension).with(options)
|
296
|
+
register_hook :append, :before, *args, &block
|
269
297
|
end
|
270
298
|
|
271
299
|
alias_method :append_before, :before
|
@@ -275,8 +303,7 @@ module RSpec
|
|
275
303
|
#
|
276
304
|
# See #before for scoping semantics.
|
277
305
|
def prepend_before(*args, &block)
|
278
|
-
|
279
|
-
hooks[:before][scope].unshift block.extend(BeforeHookExtension).with(options)
|
306
|
+
register_hook :prepend, :before, *args, &block
|
280
307
|
end
|
281
308
|
|
282
309
|
# @api public
|
@@ -328,8 +355,7 @@ module RSpec
|
|
328
355
|
# Similarly, if more than one `after` is declared within any one scope,
|
329
356
|
# they are run in reverse order of that in which they are declared.
|
330
357
|
def after(*args, &block)
|
331
|
-
|
332
|
-
hooks[:after][scope].unshift block.extend(AfterHookExtension).with(options)
|
358
|
+
register_hook :prepend, :after, *args, &block
|
333
359
|
end
|
334
360
|
|
335
361
|
alias_method :prepend_after, :after
|
@@ -339,8 +365,7 @@ module RSpec
|
|
339
365
|
#
|
340
366
|
# See #after for scoping semantics.
|
341
367
|
def append_after(*args, &block)
|
342
|
-
|
343
|
-
hooks[:after][scope] << block.extend(AfterHookExtension).with(options)
|
368
|
+
register_hook :append, :after, *args, &block
|
344
369
|
end
|
345
370
|
|
346
371
|
# @api public
|
@@ -388,8 +413,7 @@ module RSpec
|
|
388
413
|
# around(:each) {|ex| FakeFS(&ex)}
|
389
414
|
#
|
390
415
|
def around(*args, &block)
|
391
|
-
|
392
|
-
hooks[:around][scope].unshift block.extend(AroundHookExtension).with(options)
|
416
|
+
register_hook :prepend, :around, *args, &block
|
393
417
|
end
|
394
418
|
|
395
419
|
# @private
|
@@ -407,6 +431,14 @@ module RSpec
|
|
407
431
|
|
408
432
|
private
|
409
433
|
|
434
|
+
SCOPES = [:each, :all, :suite]
|
435
|
+
|
436
|
+
EXTENSIONS = {
|
437
|
+
:before => BeforeHookExtension,
|
438
|
+
:after => AfterHookExtension,
|
439
|
+
:around => AroundHookExtension
|
440
|
+
}
|
441
|
+
|
410
442
|
def before_all_hooks_for(group)
|
411
443
|
GroupHookCollection.new(hooks[:before][:all]).for(group)
|
412
444
|
end
|
@@ -423,6 +455,11 @@ module RSpec
|
|
423
455
|
HookCollection.new(ancestors.map {|a| a.hooks[:after][:each]}.flatten).for(example)
|
424
456
|
end
|
425
457
|
|
458
|
+
def register_hook prepend_or_append, hook, *args, &block
|
459
|
+
scope, options = scope_and_options_from(*args)
|
460
|
+
hooks[hook][scope].send(prepend_or_append, block.extend(EXTENSIONS[hook]).with(options))
|
461
|
+
end
|
462
|
+
|
426
463
|
def find_hook(hook, scope, example_or_group, initial_procsy)
|
427
464
|
case [hook, scope]
|
428
465
|
when [:before, :all]
|
@@ -440,8 +477,6 @@ module RSpec
|
|
440
477
|
end
|
441
478
|
end
|
442
479
|
|
443
|
-
SCOPES = [:each, :all, :suite]
|
444
|
-
|
445
480
|
def scope_and_options_from(*args)
|
446
481
|
return extract_scope_from(args), build_metadata_hash_from(args)
|
447
482
|
end
|