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.
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