rspec-core 2.10.1 → 2.11.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.
Files changed (37) hide show
  1. data/Changelog.md +35 -2
  2. data/README.md +14 -13
  3. data/features/command_line/example_name_option.feature +15 -0
  4. data/features/helper_methods/modules.feature +3 -3
  5. data/lib/rspec/core.rb +6 -2
  6. data/lib/rspec/core/configuration.rb +61 -26
  7. data/lib/rspec/core/configuration_options.rb +5 -1
  8. data/lib/rspec/core/deprecation.rb +0 -15
  9. data/lib/rspec/core/drb_command_line.rb +3 -0
  10. data/lib/rspec/core/drb_options.rb +3 -1
  11. data/lib/rspec/core/dsl.rb +4 -2
  12. data/lib/rspec/core/example.rb +85 -23
  13. data/lib/rspec/core/example_group.rb +103 -78
  14. data/lib/rspec/core/hooks.rb +68 -33
  15. data/lib/rspec/core/let.rb +0 -1
  16. data/lib/rspec/core/mocking/with_mocha.rb +10 -4
  17. data/lib/rspec/core/option_parser.rb +3 -2
  18. data/lib/rspec/core/project_initializer.rb +7 -1
  19. data/lib/rspec/core/runner.rb +2 -2
  20. data/lib/rspec/core/shared_context.rb +2 -2
  21. data/lib/rspec/core/shared_example_group.rb +38 -14
  22. data/lib/rspec/core/subject.rb +67 -52
  23. data/lib/rspec/core/version.rb +1 -1
  24. data/spec/rspec/core/command_line_spec.rb +68 -126
  25. data/spec/rspec/core/configuration_options_spec.rb +20 -4
  26. data/spec/rspec/core/configuration_spec.rb +61 -21
  27. data/spec/rspec/core/drb_command_line_spec.rb +1 -0
  28. data/spec/rspec/core/drb_options_spec.rb +1 -0
  29. data/spec/rspec/core/dsl_spec.rb +17 -0
  30. data/spec/rspec/core/example_group_spec.rb +19 -11
  31. data/spec/rspec/core/example_spec.rb +34 -0
  32. data/spec/rspec/core/option_parser_spec.rb +2 -1
  33. data/spec/rspec/core/shared_example_group_spec.rb +9 -9
  34. data/spec/rspec/core/subject_spec.rb +14 -0
  35. data/spec/rspec/core_spec.rb +18 -8
  36. data/spec/spec_helper.rb +1 -2
  37. 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 rspec-core.
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
- define_example_method :focused, :focused => true, :focus => true
70
- define_example_method :focus, :focused => true, :focus => true
71
-
72
- define_example_method :pending, :pending => true
73
- define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
74
- define_example_method :xit, :pending => 'Temporarily disabled with xit'
75
- define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
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
- class << self
78
- alias_method :alias_example_to, :define_example_method
79
- end
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
- # @private
82
- def self.define_nested_shared_group_method(new_name, report_label=nil)
83
- module_eval(<<-END_RUBY, __FILE__, __LINE__)
84
- def self.#{new_name}(name, *args, &customization_block)
85
- group = describe("#{report_label || "it should behave like"} \#{name}") do
86
- find_and_eval_shared("examples", name, *args, &customization_block)
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
- group.metadata[:shared_group_name] = name
89
- group
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
- class << self
97
- alias_method :alias_it_should_behave_like_to, :define_nested_shared_group_method
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
- alias_it_should_behave_like_to :it_behaves_like, "behaves like"
101
-
102
- # Includes shared content declared with `name`.
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
- block_given? ? block_not_supported("context") : find_and_eval_shared("context", name, *args)
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 declared with `name`.
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
- block_given? ? block_not_supported("examples") : find_and_eval_shared("examples", name, *args)
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
- shared_block = world.shared_example_groups[name]
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 [ExampleGroup](ExampleGroup)
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
- [:before, :after, :around].each do |_when|
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
- EOS
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 [Example](Example) object that wraps this instance of
429
+ # Returns the {Example} object that wraps this instance of
405
430
  # `ExampleGroup`
406
431
  attr_accessor :example
407
432
 
408
- # @deprecated use [example](ExampleGroup#example-instance_method)
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
@@ -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 => [], :all => [], :suite => HookCollection.new },
104
- :after => { :each => [], :all => [], :suite => HookCollection.new }
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 [ExampleGroup](ExampleGroup) to which they apply, but
130
- # they can also be shared across multiple groups.
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 [ExampleGroup](ExampleGroup)
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 [ExampleGroup](ExampleGroup)
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
- scope, options = scope_and_options_from(*args)
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
- scope, options = scope_and_options_from(*args)
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
- scope, options = scope_and_options_from(*args)
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
- scope, options = scope_and_options_from(*args)
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
- scope, options = scope_and_options_from(*args)
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