rspec-core 3.1.7 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +1 -0
- data/Changelog.md +84 -0
- data/README.md +10 -1
- data/lib/rspec/core.rb +28 -8
- data/lib/rspec/core/backport_random.rb +12 -9
- data/lib/rspec/core/configuration.rb +350 -112
- data/lib/rspec/core/configuration_options.rb +14 -7
- data/lib/rspec/core/dsl.rb +7 -4
- data/lib/rspec/core/example.rb +86 -50
- data/lib/rspec/core/example_group.rb +247 -86
- data/lib/rspec/core/filter_manager.rb +38 -93
- data/lib/rspec/core/flat_map.rb +4 -4
- data/lib/rspec/core/formatters.rb +10 -6
- data/lib/rspec/core/formatters/base_formatter.rb +7 -4
- data/lib/rspec/core/formatters/base_text_formatter.rb +12 -12
- data/lib/rspec/core/formatters/console_codes.rb +8 -7
- data/lib/rspec/core/formatters/deprecation_formatter.rb +5 -3
- data/lib/rspec/core/formatters/documentation_formatter.rb +10 -4
- data/lib/rspec/core/formatters/helpers.rb +6 -4
- data/lib/rspec/core/formatters/html_formatter.rb +13 -8
- data/lib/rspec/core/formatters/html_printer.rb +26 -10
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -7
- data/lib/rspec/core/formatters/protocol.rb +27 -18
- data/lib/rspec/core/formatters/snippet_extractor.rb +14 -7
- data/lib/rspec/core/hooks.rb +252 -211
- data/lib/rspec/core/memoized_helpers.rb +16 -16
- data/lib/rspec/core/metadata.rb +67 -28
- data/lib/rspec/core/metadata_filter.rb +151 -24
- data/lib/rspec/core/minitest_assertions_adapter.rb +5 -2
- data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
- data/lib/rspec/core/mocking_adapters/mocha.rb +8 -8
- data/lib/rspec/core/notifications.rb +155 -94
- data/lib/rspec/core/option_parser.rb +16 -10
- data/lib/rspec/core/pending.rb +11 -9
- data/lib/rspec/core/project_initializer.rb +1 -1
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +10 -8
- data/lib/rspec/core/rake_task.rb +37 -52
- data/lib/rspec/core/reporter.rb +30 -7
- data/lib/rspec/core/ruby_project.rb +12 -4
- data/lib/rspec/core/runner.rb +5 -8
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/shared_example_group.rb +41 -15
- data/lib/rspec/core/test_unit_assertions_adapter.rb +3 -3
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +2 -2
- data/lib/rspec/core/world.rb +12 -28
- metadata +44 -31
- metadata.gz.sig +0 -0
@@ -84,15 +84,22 @@ module RSpec
|
|
84
84
|
# set before it.
|
85
85
|
:default_path,
|
86
86
|
|
87
|
-
# These must be set before `requires` to support checking
|
88
|
-
# from within `spec_helper.rb` when a
|
87
|
+
# These must be set before `requires` to support checking
|
88
|
+
# `config.files_to_run` from within `spec_helper.rb` when a
|
89
|
+
# `-rspec_helper` option is used.
|
89
90
|
:files_or_directories_to_run, :pattern, :exclude_pattern,
|
90
91
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
|
95
|
-
|
92
|
+
# Necessary so that the `--seed` option is applied before requires,
|
93
|
+
# in case required files do something with the provided seed.
|
94
|
+
# (such as seed global randomization with it).
|
95
|
+
:order,
|
96
|
+
|
97
|
+
# In general, we want to require the specified files as early as
|
98
|
+
# possible. The `--require` option is specifically intended to allow
|
99
|
+
# early requires. For later requires, they can just put the require in
|
100
|
+
# their spec files, but `--require` provides a unique opportunity for
|
101
|
+
# users to instruct RSpec to load an extension file early for maximum
|
102
|
+
# flexibility.
|
96
103
|
:requires
|
97
104
|
]
|
98
105
|
|
data/lib/rspec/core/dsl.rb
CHANGED
@@ -8,7 +8,7 @@ module RSpec
|
|
8
8
|
# By default the methods `describe`, `context` and `example_group`
|
9
9
|
# are exposed. These methods define a named context for one or
|
10
10
|
# more examples. The given block is evaluated in the context of
|
11
|
-
# a generated subclass of {RSpec::Core::ExampleGroup}
|
11
|
+
# a generated subclass of {RSpec::Core::ExampleGroup}.
|
12
12
|
#
|
13
13
|
# ## Examples:
|
14
14
|
#
|
@@ -35,6 +35,8 @@ module RSpec
|
|
35
35
|
|
36
36
|
# @private
|
37
37
|
def self.expose_example_group_alias(name)
|
38
|
+
return if example_group_aliases.include?(name)
|
39
|
+
|
38
40
|
example_group_aliases << name
|
39
41
|
|
40
42
|
(class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block|
|
@@ -49,7 +51,7 @@ module RSpec
|
|
49
51
|
attr_accessor :top_level
|
50
52
|
end
|
51
53
|
|
52
|
-
# Adds the describe method to Module and the top level binding
|
54
|
+
# Adds the describe method to Module and the top level binding.
|
53
55
|
# @api private
|
54
56
|
def self.expose_globally!
|
55
57
|
return if exposed_globally?
|
@@ -61,7 +63,7 @@ module RSpec
|
|
61
63
|
@exposed_globally = true
|
62
64
|
end
|
63
65
|
|
64
|
-
# Removes the describe method from Module and the top level binding
|
66
|
+
# Removes the describe method from Module and the top level binding.
|
65
67
|
# @api private
|
66
68
|
def self.remove_globally!
|
67
69
|
return unless exposed_globally?
|
@@ -76,6 +78,7 @@ module RSpec
|
|
76
78
|
# @private
|
77
79
|
def self.expose_example_group_alias_globally(method_name)
|
78
80
|
change_global_dsl do
|
81
|
+
remove_method(method_name) if method_defined?(method_name)
|
79
82
|
define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) }
|
80
83
|
end
|
81
84
|
end
|
@@ -89,5 +92,5 @@ module RSpec
|
|
89
92
|
end
|
90
93
|
end
|
91
94
|
|
92
|
-
#
|
95
|
+
# Capture main without an eval.
|
93
96
|
::RSpec::Core::DSL.top_level = self
|
data/lib/rspec/core/example.rb
CHANGED
@@ -44,14 +44,15 @@ module RSpec
|
|
44
44
|
class Example
|
45
45
|
# @private
|
46
46
|
#
|
47
|
-
# Used to define methods that delegate to this example's metadata
|
47
|
+
# Used to define methods that delegate to this example's metadata.
|
48
48
|
def self.delegate_to_metadata(key)
|
49
49
|
define_method(key) { @metadata[key] }
|
50
50
|
end
|
51
51
|
|
52
52
|
# @return [ExecutionResult] represents the result of running this example.
|
53
53
|
delegate_to_metadata :execution_result
|
54
|
-
# @return [String] the relative path to the file where this example was
|
54
|
+
# @return [String] the relative path to the file where this example was
|
55
|
+
# defined.
|
55
56
|
delegate_to_metadata :file_path
|
56
57
|
# @return [String] the full description (including the docstrings of
|
57
58
|
# all parent example groups).
|
@@ -59,22 +60,22 @@ module RSpec
|
|
59
60
|
# @return [String] the exact source location of this example in a form
|
60
61
|
# like `./path/to/spec.rb:17`
|
61
62
|
delegate_to_metadata :location
|
62
|
-
# @return [Boolean] flag that indicates that the example is not expected
|
63
|
-
# It will be run and will either have a pending result (if a
|
64
|
-
# or a failed result (if no failure occurs).
|
63
|
+
# @return [Boolean] flag that indicates that the example is not expected
|
64
|
+
# to pass. It will be run and will either have a pending result (if a
|
65
|
+
# failure occurs) or a failed result (if no failure occurs).
|
65
66
|
delegate_to_metadata :pending
|
66
67
|
# @return [Boolean] flag that will cause the example to not run.
|
67
68
|
# The {ExecutionResult} status will be `:pending`.
|
68
69
|
delegate_to_metadata :skip
|
69
70
|
|
70
71
|
# Returns the string submitted to `example` or its aliases (e.g.
|
71
|
-
# `specify`, `it`, etc).
|
72
|
-
# do_something }`) it returns the message generated
|
73
|
-
# there is one, otherwise returns a message including
|
74
|
-
# example.
|
72
|
+
# `specify`, `it`, etc). If no string is submitted (e.g.
|
73
|
+
# `it { is_expected.to do_something }`) it returns the message generated
|
74
|
+
# by the matcher if there is one, otherwise returns a message including
|
75
|
+
# the location of the example.
|
75
76
|
def description
|
76
77
|
description = if metadata[:description].to_s.empty?
|
77
|
-
|
78
|
+
location_description
|
78
79
|
else
|
79
80
|
metadata[:description]
|
80
81
|
end
|
@@ -82,10 +83,28 @@ module RSpec
|
|
82
83
|
RSpec.configuration.format_docstrings_block.call(description)
|
83
84
|
end
|
84
85
|
|
86
|
+
# Returns a description of the example that always includes the location.
|
87
|
+
def inspect_output
|
88
|
+
inspect_output = "\"#{description}\""
|
89
|
+
unless metadata[:description].to_s.empty?
|
90
|
+
inspect_output << " (#{location})"
|
91
|
+
end
|
92
|
+
inspect_output
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the argument that can be passed to the `rspec` command to rerun this example.
|
96
|
+
def rerun_argument
|
97
|
+
loaded_spec_files = RSpec.configuration.loaded_spec_files
|
98
|
+
|
99
|
+
Metadata.ascending(metadata) do |meta|
|
100
|
+
return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
85
104
|
# @attr_reader
|
86
105
|
#
|
87
106
|
# Returns the first exception raised in the context of running this
|
88
|
-
# example (nil if no exception is raised)
|
107
|
+
# example (nil if no exception is raised).
|
89
108
|
attr_reader :exception
|
90
109
|
|
91
110
|
# @attr_reader
|
@@ -105,10 +124,14 @@ module RSpec
|
|
105
124
|
attr_accessor :clock
|
106
125
|
|
107
126
|
# Creates a new instance of Example.
|
108
|
-
# @param example_group_class [Class] the subclass of ExampleGroup in which
|
109
|
-
#
|
110
|
-
# @param
|
111
|
-
#
|
127
|
+
# @param example_group_class [Class] the subclass of ExampleGroup in which
|
128
|
+
# this Example is declared
|
129
|
+
# @param description [String] the String passed to the `it` method (or
|
130
|
+
# alias)
|
131
|
+
# @param user_metadata [Hash] additional args passed to `it` to be used as
|
132
|
+
# metadata
|
133
|
+
# @param example_block [Proc] the block of code that represents the
|
134
|
+
# example
|
112
135
|
# @api private
|
113
136
|
def initialize(example_group_class, description, user_metadata, example_block=nil)
|
114
137
|
@example_group_class = example_group_class
|
@@ -137,6 +160,8 @@ module RSpec
|
|
137
160
|
# @param example_group_instance the instance of an ExampleGroup subclass
|
138
161
|
def run(example_group_instance, reporter)
|
139
162
|
@example_group_instance = example_group_instance
|
163
|
+
hooks.register_global_singleton_context_hooks(self, RSpec.configuration.hooks)
|
164
|
+
RSpec.configuration.configure_example(self)
|
140
165
|
RSpec.current_example = self
|
141
166
|
|
142
167
|
start(reporter)
|
@@ -146,7 +171,7 @@ module RSpec
|
|
146
171
|
if skipped?
|
147
172
|
Pending.mark_pending! self, skip
|
148
173
|
elsif !RSpec.configuration.dry_run?
|
149
|
-
|
174
|
+
with_around_and_singleton_context_hooks do
|
150
175
|
begin
|
151
176
|
run_before_example
|
152
177
|
@example_group_instance.instance_exec(self, &@example_block)
|
@@ -171,7 +196,7 @@ module RSpec
|
|
171
196
|
rescue Exception => e
|
172
197
|
set_exception(e)
|
173
198
|
ensure
|
174
|
-
@example_group_instance
|
199
|
+
ExampleGroup.each_instance_variable_for_example(@example_group_instance) do |ivar|
|
175
200
|
@example_group_instance.instance_variable_set(ivar, nil)
|
176
201
|
end
|
177
202
|
@example_group_instance = nil
|
@@ -194,7 +219,7 @@ module RSpec
|
|
194
219
|
# if ex.metadata[:key] == :some_value && some_global_condition
|
195
220
|
# raise "some message"
|
196
221
|
# end
|
197
|
-
# ex.run # run delegates to ex.call
|
222
|
+
# ex.run # run delegates to ex.call.
|
198
223
|
# end
|
199
224
|
# end
|
200
225
|
#
|
@@ -223,7 +248,8 @@ module RSpec
|
|
223
248
|
end
|
224
249
|
alias run call
|
225
250
|
|
226
|
-
# Provides a wrapped proc that will update our `executed?` state when
|
251
|
+
# Provides a wrapped proc that will update our `executed?` state when
|
252
|
+
# executed.
|
227
253
|
def to_proc
|
228
254
|
method(:call).to_proc
|
229
255
|
end
|
@@ -250,21 +276,6 @@ module RSpec
|
|
250
276
|
end
|
251
277
|
end
|
252
278
|
|
253
|
-
# @private
|
254
|
-
def any_apply?(filters)
|
255
|
-
MetadataFilter.any_apply?(filters, metadata)
|
256
|
-
end
|
257
|
-
|
258
|
-
# @private
|
259
|
-
def all_apply?(filters)
|
260
|
-
MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters)
|
261
|
-
end
|
262
|
-
|
263
|
-
# @private
|
264
|
-
def around_example_hooks
|
265
|
-
@around_example_hooks ||= example_group.hooks.around_example_hooks_for(self)
|
266
|
-
end
|
267
|
-
|
268
279
|
# @private
|
269
280
|
#
|
270
281
|
# Used internally to set an exception in an after hook, which
|
@@ -303,7 +314,7 @@ module RSpec
|
|
303
314
|
# @private
|
304
315
|
#
|
305
316
|
# Used internally to skip without actually executing the example when
|
306
|
-
# skip is used in before(:context)
|
317
|
+
# skip is used in before(:context).
|
307
318
|
def skip_with_exception(reporter, exception)
|
308
319
|
start(reporter)
|
309
320
|
Pending.mark_skipped! self, exception.argument
|
@@ -324,12 +335,12 @@ module RSpec
|
|
324
335
|
|
325
336
|
private
|
326
337
|
|
327
|
-
def
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
338
|
+
def hooks
|
339
|
+
example_group_instance.singleton_class.hooks
|
340
|
+
end
|
341
|
+
|
342
|
+
def with_around_example_hooks
|
343
|
+
hooks.run(:around, :example, self) { yield }
|
333
344
|
rescue Exception => e
|
334
345
|
set_exception(e, "in an `around(:example)` hook")
|
335
346
|
end
|
@@ -365,15 +376,21 @@ module RSpec
|
|
365
376
|
|
366
377
|
def run_before_example
|
367
378
|
@example_group_instance.setup_mocks_for_rspec
|
368
|
-
|
379
|
+
hooks.run(:before, :example, self)
|
380
|
+
end
|
381
|
+
|
382
|
+
def with_around_and_singleton_context_hooks
|
383
|
+
singleton_context_hooks_host = example_group_instance.singleton_class
|
384
|
+
singleton_context_hooks_host.run_before_context_hooks(example_group_instance)
|
385
|
+
with_around_example_hooks { yield }
|
386
|
+
ensure
|
387
|
+
singleton_context_hooks_host.run_after_context_hooks(example_group_instance)
|
369
388
|
end
|
370
389
|
|
371
390
|
def run_after_example
|
372
|
-
|
391
|
+
assign_generated_description if defined?(::RSpec::Matchers)
|
392
|
+
hooks.run(:after, :example, self)
|
373
393
|
verify_mocks
|
374
|
-
assign_generated_description if RSpec.configuration.expecting_with_rspec?
|
375
|
-
rescue Exception => e
|
376
|
-
set_exception(e, "in an `after(:example)` hook")
|
377
394
|
ensure
|
378
395
|
@example_group_instance.teardown_mocks_for_rspec
|
379
396
|
end
|
@@ -383,6 +400,7 @@ module RSpec
|
|
383
400
|
rescue Exception => e
|
384
401
|
if pending?
|
385
402
|
execution_result.pending_fixed = false
|
403
|
+
execution_result.pending_exception = e
|
386
404
|
@exception = nil
|
387
405
|
else
|
388
406
|
set_exception(e)
|
@@ -394,16 +412,25 @@ module RSpec
|
|
394
412
|
end
|
395
413
|
|
396
414
|
def assign_generated_description
|
397
|
-
if metadata[:description].empty? && (description =
|
415
|
+
if metadata[:description].empty? && (description = generate_description)
|
398
416
|
metadata[:description] = description
|
399
417
|
metadata[:full_description] << description
|
400
418
|
end
|
401
|
-
rescue Exception => e
|
402
|
-
set_exception(e, "while assigning the example description")
|
403
419
|
ensure
|
404
420
|
RSpec::Matchers.clear_generated_description
|
405
421
|
end
|
406
422
|
|
423
|
+
def generate_description
|
424
|
+
RSpec::Matchers.generated_description
|
425
|
+
rescue Exception => e
|
426
|
+
location_description + " (Got an error when generating description " \
|
427
|
+
"from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})"
|
428
|
+
end
|
429
|
+
|
430
|
+
def location_description
|
431
|
+
"example at #{location}"
|
432
|
+
end
|
433
|
+
|
407
434
|
def skip_message
|
408
435
|
if String === skip
|
409
436
|
skip
|
@@ -448,6 +475,15 @@ module RSpec
|
|
448
475
|
|
449
476
|
alias pending_fixed? pending_fixed
|
450
477
|
|
478
|
+
# @return [Boolean] Indicates if the example was completely skipped
|
479
|
+
# (typically done via `:skip` metadata or the `skip` method). Skipped examples
|
480
|
+
# will have a `:pending` result. A `:pending` result can also come from examples
|
481
|
+
# that were marked as `:pending`, which causes them to be run, and produces a
|
482
|
+
# `:failed` result if the example passes.
|
483
|
+
def example_skipped?
|
484
|
+
status == :pending && !pending_exception
|
485
|
+
end
|
486
|
+
|
451
487
|
# @api private
|
452
488
|
# Records the finished status of the example.
|
453
489
|
def record_finished(status, finished_at)
|
@@ -493,7 +529,7 @@ module RSpec
|
|
493
529
|
super(AnonymousExampleGroup, "", {})
|
494
530
|
end
|
495
531
|
|
496
|
-
# To ensure we don't silence errors
|
532
|
+
# To ensure we don't silence errors.
|
497
533
|
def set_exception(exception, _context=nil)
|
498
534
|
raise exception
|
499
535
|
end
|
@@ -3,7 +3,7 @@ RSpec::Support.require_rspec_support 'recursive_const_methods'
|
|
3
3
|
module RSpec
|
4
4
|
module Core
|
5
5
|
# ExampleGroup and {Example} are the main structural elements of
|
6
|
-
# rspec-core.
|
6
|
+
# rspec-core. Consider this example:
|
7
7
|
#
|
8
8
|
# describe Thing do
|
9
9
|
# it "does something" do
|
@@ -17,13 +17,13 @@ module RSpec
|
|
17
17
|
#
|
18
18
|
# Example group bodies (e.g. `describe` or `context` blocks) are evaluated
|
19
19
|
# in the context of a new subclass of ExampleGroup. Individual examples are
|
20
|
-
# evaluated in the context of an instance of the specific ExampleGroup
|
21
|
-
# to which they belong.
|
20
|
+
# evaluated in the context of an instance of the specific ExampleGroup
|
21
|
+
# subclass to which they belong.
|
22
22
|
#
|
23
23
|
# Besides the class methods defined here, there are other interesting macros
|
24
|
-
# defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
|
25
|
-
# There are additional instance methods available to
|
26
|
-
# {MemoizedHelpers} and {Pending}.
|
24
|
+
# defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
|
25
|
+
# {SharedExampleGroup}. There are additional instance methods available to
|
26
|
+
# your examples defined in {MemoizedHelpers} and {Pending}.
|
27
27
|
class ExampleGroup
|
28
28
|
extend Hooks
|
29
29
|
|
@@ -32,10 +32,11 @@ module RSpec
|
|
32
32
|
include Pending
|
33
33
|
extend SharedExampleGroup
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
(
|
35
|
+
# @private
|
36
|
+
def self.idempotently_define_singleton_method(name, &definition)
|
37
|
+
(class << self; self; end).module_exec do
|
38
|
+
remove_method(name) if method_defined?(name)
|
39
|
+
define_method(name, &definition)
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
@@ -44,7 +45,21 @@ module RSpec
|
|
44
45
|
# The [Metadata](Metadata) object associated with this group.
|
45
46
|
# @see Metadata
|
46
47
|
def self.metadata
|
47
|
-
@metadata
|
48
|
+
@metadata ||= nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# Temporarily replace the provided metadata.
|
52
|
+
# Intended primarily to allow an example group's singleton class
|
53
|
+
# to return the metadata of the example that it exists for. This
|
54
|
+
# is necessary for shared example group inclusion to work properly
|
55
|
+
# with singleton example groups.
|
56
|
+
# @private
|
57
|
+
def self.with_replaced_metadata(meta)
|
58
|
+
orig_metadata = metadata
|
59
|
+
@metadata = meta
|
60
|
+
yield
|
61
|
+
ensure
|
62
|
+
@metadata = orig_metadata
|
48
63
|
end
|
49
64
|
|
50
65
|
# @private
|
@@ -56,7 +71,7 @@ module RSpec
|
|
56
71
|
# @private
|
57
72
|
def self.delegate_to_metadata(*names)
|
58
73
|
names.each do |name|
|
59
|
-
|
74
|
+
idempotently_define_singleton_method(name) { metadata.fetch(name) }
|
60
75
|
end
|
61
76
|
end
|
62
77
|
|
@@ -77,7 +92,6 @@ module RSpec
|
|
77
92
|
# end
|
78
93
|
# end
|
79
94
|
#
|
80
|
-
#
|
81
95
|
def described_class
|
82
96
|
self.class.described_class
|
83
97
|
end
|
@@ -89,9 +103,20 @@ module RSpec
|
|
89
103
|
# @private
|
90
104
|
# @macro [attach] define_example_method
|
91
105
|
# @!scope class
|
92
|
-
# @
|
93
|
-
# @
|
94
|
-
#
|
106
|
+
# @overload $1
|
107
|
+
# @overload $1(&example_implementation)
|
108
|
+
# @param example_implementation [Block] The implementation of the example.
|
109
|
+
# @overload $1(doc_string, *metadata_keys, metadata={})
|
110
|
+
# @param doc_string [String] The example's doc string.
|
111
|
+
# @param metadata [Hash] Metadata for the example.
|
112
|
+
# @param metadata_keys [Array<Symbol>] Metadata tags for the example.
|
113
|
+
# Will be transformed into hash entries with `true` values.
|
114
|
+
# @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
|
115
|
+
# @param doc_string [String] The example's doc string.
|
116
|
+
# @param metadata [Hash] Metadata for the example.
|
117
|
+
# @param metadata_keys [Array<Symbol>] Metadata tags for the example.
|
118
|
+
# Will be transformed into hash entries with `true` values.
|
119
|
+
# @param example_implementation [Block] The implementation of the example.
|
95
120
|
# @yield [Example] the example object
|
96
121
|
# @example
|
97
122
|
# $1 do
|
@@ -100,6 +125,9 @@ module RSpec
|
|
100
125
|
# $1 "does something" do
|
101
126
|
# end
|
102
127
|
#
|
128
|
+
# $1 "does something", :slow, :uses_js do
|
129
|
+
# end
|
130
|
+
#
|
103
131
|
# $1 "does something", :with => 'additional metadata' do
|
104
132
|
# end
|
105
133
|
#
|
@@ -107,7 +135,7 @@ module RSpec
|
|
107
135
|
# # ex is the Example object that contains metadata about the example
|
108
136
|
# end
|
109
137
|
def self.define_example_method(name, extra_options={})
|
110
|
-
|
138
|
+
idempotently_define_singleton_method(name) do |*all_args, &block|
|
111
139
|
desc, *args = *all_args
|
112
140
|
|
113
141
|
options = Metadata.build_hash_from(args)
|
@@ -134,25 +162,25 @@ module RSpec
|
|
134
162
|
# end
|
135
163
|
define_example_method :specify
|
136
164
|
|
137
|
-
# Shortcut to define an example with `:focus => true
|
165
|
+
# Shortcut to define an example with `:focus => true`.
|
138
166
|
# @see example
|
139
167
|
define_example_method :focus, :focus => true
|
140
|
-
# Shortcut to define an example with `:focus => true
|
168
|
+
# Shortcut to define an example with `:focus => true`.
|
141
169
|
# @see example
|
142
170
|
define_example_method :fexample, :focus => true
|
143
|
-
# Shortcut to define an example with `:focus => true
|
171
|
+
# Shortcut to define an example with `:focus => true`.
|
144
172
|
# @see example
|
145
173
|
define_example_method :fit, :focus => true
|
146
|
-
# Shortcut to define an example with `:focus => true
|
174
|
+
# Shortcut to define an example with `:focus => true`.
|
147
175
|
# @see example
|
148
176
|
define_example_method :fspecify, :focus => true
|
149
|
-
# Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'
|
177
|
+
# Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
|
150
178
|
# @see example
|
151
179
|
define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
|
152
|
-
# Shortcut to define an example with `:skip => 'Temporarily skipped with xit'
|
180
|
+
# Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
|
153
181
|
# @see example
|
154
182
|
define_example_method :xit, :skip => 'Temporarily skipped with xit'
|
155
|
-
# Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'
|
183
|
+
# Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
|
156
184
|
# @see example
|
157
185
|
define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
|
158
186
|
# Shortcut to define an example with `:skip => true`
|
@@ -167,11 +195,17 @@ module RSpec
|
|
167
195
|
# @!group Defining Example Groups
|
168
196
|
|
169
197
|
# @private
|
170
|
-
# @macro [attach]
|
198
|
+
# @macro [attach] define_example_group_method
|
171
199
|
# @!scope class
|
172
|
-
# @
|
173
|
-
# @
|
174
|
-
#
|
200
|
+
# @overload $1
|
201
|
+
# @overload $1(&example_group_definition)
|
202
|
+
# @param example_group_definition [Block] The definition of the example group.
|
203
|
+
# @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
|
204
|
+
# @param doc_string [String] The group's doc string.
|
205
|
+
# @param metadata [Hash] Metadata for the group.
|
206
|
+
# @param metadata_keys [Array<Symbol>] Metadata tags for the group.
|
207
|
+
# Will be transformed into hash entries with `true` values.
|
208
|
+
# @param example_group_definition [Block] The definition of the example group.
|
175
209
|
#
|
176
210
|
# Generates a subclass of this example group which inherits
|
177
211
|
# everything except the examples themselves.
|
@@ -195,7 +229,7 @@ module RSpec
|
|
195
229
|
#
|
196
230
|
# @see DSL#describe
|
197
231
|
def self.define_example_group_method(name, metadata={})
|
198
|
-
|
232
|
+
idempotently_define_singleton_method(name) do |*args, &example_group_block|
|
199
233
|
thread_data = RSpec.thread_local_metadata
|
200
234
|
top_level = self == ExampleGroup
|
201
235
|
|
@@ -230,8 +264,8 @@ module RSpec
|
|
230
264
|
|
231
265
|
define_example_group_method :example_group
|
232
266
|
|
233
|
-
# An alias of `example_group`. Generally used when grouping
|
234
|
-
#
|
267
|
+
# An alias of `example_group`. Generally used when grouping examples by a
|
268
|
+
# thing you are describing (e.g. an object, class or method).
|
235
269
|
# @see example_group
|
236
270
|
define_example_group_method :describe
|
237
271
|
|
@@ -266,12 +300,12 @@ module RSpec
|
|
266
300
|
#
|
267
301
|
# @see SharedExampleGroup
|
268
302
|
def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
|
269
|
-
|
270
|
-
# Pass :caller so the :location metadata is set properly
|
271
|
-
#
|
303
|
+
idempotently_define_singleton_method(new_name) do |name, *args, &customization_block|
|
304
|
+
# Pass :caller so the :location metadata is set properly.
|
305
|
+
# Otherwise, it'll be set to the next line because that's
|
272
306
|
# the block's source_location.
|
273
|
-
group = example_group("#{report_label} #{name}", :caller => caller) do
|
274
|
-
find_and_eval_shared("examples", name, *args, &customization_block)
|
307
|
+
group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do
|
308
|
+
find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block)
|
275
309
|
end
|
276
310
|
group.metadata[:shared_group_name] = name
|
277
311
|
group
|
@@ -287,32 +321,36 @@ module RSpec
|
|
287
321
|
|
288
322
|
# Includes shared content mapped to `name` directly in the group in which
|
289
323
|
# it is declared, as opposed to `it_behaves_like`, which creates a nested
|
290
|
-
# group. If given a block, that block is also eval'd in the current
|
324
|
+
# group. If given a block, that block is also eval'd in the current
|
325
|
+
# context.
|
291
326
|
#
|
292
327
|
# @see SharedExampleGroup
|
293
328
|
def self.include_context(name, *args, &block)
|
294
|
-
find_and_eval_shared("context", name, *args, &block)
|
329
|
+
find_and_eval_shared("context", name, caller.first, *args, &block)
|
295
330
|
end
|
296
331
|
|
297
332
|
# Includes shared content mapped to `name` directly in the group in which
|
298
333
|
# it is declared, as opposed to `it_behaves_like`, which creates a nested
|
299
|
-
# group. If given a block, that block is also eval'd in the current
|
334
|
+
# group. If given a block, that block is also eval'd in the current
|
335
|
+
# context.
|
300
336
|
#
|
301
337
|
# @see SharedExampleGroup
|
302
338
|
def self.include_examples(name, *args, &block)
|
303
|
-
find_and_eval_shared("examples", name, *args, &block)
|
339
|
+
find_and_eval_shared("examples", name, caller.first, *args, &block)
|
304
340
|
end
|
305
341
|
|
306
342
|
# @private
|
307
|
-
def self.find_and_eval_shared(label, name, *args, &customization_block)
|
343
|
+
def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
|
308
344
|
shared_block = RSpec.world.shared_example_group_registry.find(parent_groups, name)
|
309
345
|
|
310
346
|
unless shared_block
|
311
347
|
raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
|
312
348
|
end
|
313
349
|
|
314
|
-
|
315
|
-
|
350
|
+
SharedExampleGroupInclusionStackFrame.with_frame(name, Metadata.relative_path(inclusion_location)) do
|
351
|
+
module_exec(*args, &shared_block)
|
352
|
+
module_exec(&customization_block) if customization_block
|
353
|
+
end
|
316
354
|
end
|
317
355
|
|
318
356
|
# @!endgroup
|
@@ -338,10 +376,11 @@ module RSpec
|
|
338
376
|
# Ruby 1.9 has a bug that can lead to infinite recursion and a
|
339
377
|
# SystemStackError if you include a module in a superclass after
|
340
378
|
# including it in a subclass: https://gist.github.com/845896
|
341
|
-
# To prevent this, we must include any modules in
|
342
|
-
# before users create example groups and have
|
343
|
-
# the same module in a subclass of
|
344
|
-
# So we need to configure example groups
|
379
|
+
# To prevent this, we must include any modules in
|
380
|
+
# RSpec::Core::ExampleGroup before users create example groups and have
|
381
|
+
# a chance to include the same module in a subclass of
|
382
|
+
# RSpec::Core::ExampleGroup. So we need to configure example groups
|
383
|
+
# here.
|
345
384
|
ensure_example_groups_are_configured
|
346
385
|
|
347
386
|
description = args.shift
|
@@ -353,7 +392,7 @@ module RSpec
|
|
353
392
|
)
|
354
393
|
|
355
394
|
hooks.register_globals(self, RSpec.configuration.hooks)
|
356
|
-
RSpec.
|
395
|
+
RSpec.configuration.configure_group(self)
|
357
396
|
end
|
358
397
|
|
359
398
|
# @private
|
@@ -368,7 +407,8 @@ module RSpec
|
|
368
407
|
|
369
408
|
# @private
|
370
409
|
def self.descendant_filtered_examples
|
371
|
-
@descendant_filtered_examples ||= filtered_examples +
|
410
|
+
@descendant_filtered_examples ||= filtered_examples +
|
411
|
+
FlatMap.flat_map(children, &:descendant_filtered_examples)
|
372
412
|
end
|
373
413
|
|
374
414
|
# @private
|
@@ -378,7 +418,7 @@ module RSpec
|
|
378
418
|
|
379
419
|
# @private
|
380
420
|
def self.descendants
|
381
|
-
@_descendants ||= [self] +
|
421
|
+
@_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
|
382
422
|
end
|
383
423
|
|
384
424
|
## @private
|
@@ -409,47 +449,66 @@ module RSpec
|
|
409
449
|
|
410
450
|
# @private
|
411
451
|
def self.store_before_context_ivars(example_group_instance)
|
412
|
-
|
413
|
-
|
414
|
-
example_group_instance.instance_variables.each do |ivar|
|
452
|
+
each_instance_variable_for_example(example_group_instance) do |ivar|
|
415
453
|
before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
|
416
454
|
end
|
417
455
|
end
|
418
456
|
|
419
457
|
# @private
|
420
458
|
def self.run_before_context_hooks(example_group_instance)
|
421
|
-
|
422
|
-
|
423
|
-
|
459
|
+
set_ivars(example_group_instance, superclass_before_context_ivars)
|
460
|
+
|
461
|
+
ContextHookMemoizedHash::Before.isolate_for_context_hook(example_group_instance) do
|
462
|
+
hooks.run(:before, :context, example_group_instance)
|
463
|
+
end
|
464
|
+
ensure
|
465
|
+
store_before_context_ivars(example_group_instance)
|
466
|
+
end
|
424
467
|
|
425
|
-
|
426
|
-
|
468
|
+
if RUBY_VERSION.to_f >= 1.9
|
469
|
+
# @private
|
470
|
+
def self.superclass_before_context_ivars
|
471
|
+
superclass.before_context_ivars
|
472
|
+
end
|
473
|
+
else # 1.8.7
|
474
|
+
# @private
|
475
|
+
def self.superclass_before_context_ivars
|
476
|
+
if superclass.respond_to?(:before_context_ivars)
|
477
|
+
superclass.before_context_ivars
|
478
|
+
else
|
479
|
+
# `self` must be the singleton class of an ExampleGroup instance.
|
480
|
+
# On 1.8.7, the superclass of a singleton class of an instance of A
|
481
|
+
# is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor
|
482
|
+
# is A, so we can mirror 1.8.7's behavior here. Note that we have to
|
483
|
+
# search for the first that responds to `before_context_ivars`
|
484
|
+
# in case a module has been included in the singleton class.
|
485
|
+
ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
|
427
486
|
end
|
428
|
-
ensure
|
429
|
-
store_before_context_ivars(example_group_instance)
|
430
487
|
end
|
431
488
|
end
|
432
489
|
|
433
490
|
# @private
|
434
491
|
def self.run_after_context_hooks(example_group_instance)
|
435
|
-
return if descendant_filtered_examples.empty?
|
436
492
|
set_ivars(example_group_instance, before_context_ivars)
|
437
493
|
|
438
494
|
ContextHookMemoizedHash::After.isolate_for_context_hook(example_group_instance) do
|
439
495
|
hooks.run(:after, :context, example_group_instance)
|
440
496
|
end
|
497
|
+
ensure
|
498
|
+
before_context_ivars.clear
|
441
499
|
end
|
442
500
|
|
443
|
-
# Runs all the examples in this group
|
444
|
-
def self.run(reporter)
|
501
|
+
# Runs all the examples in this group.
|
502
|
+
def self.run(reporter=RSpec::Core::NullReporter.new)
|
445
503
|
if RSpec.world.wants_to_quit
|
446
504
|
RSpec.world.clear_remaining_example_groups if top_level?
|
447
505
|
return
|
448
506
|
end
|
449
507
|
reporter.example_group_started(self)
|
450
508
|
|
509
|
+
should_run_context_hooks = descendant_filtered_examples.any?
|
451
510
|
begin
|
452
|
-
run_before_context_hooks(new)
|
511
|
+
run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
|
453
512
|
result_for_this_group = run_examples(reporter)
|
454
513
|
results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
|
455
514
|
result_for_this_group && results_for_descendants
|
@@ -459,8 +518,7 @@ module RSpec
|
|
459
518
|
RSpec.world.wants_to_quit = true if fail_fast?
|
460
519
|
for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
|
461
520
|
ensure
|
462
|
-
run_after_context_hooks(new)
|
463
|
-
before_context_ivars.clear
|
521
|
+
run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
|
464
522
|
reporter.example_group_finished(self)
|
465
523
|
end
|
466
524
|
end
|
@@ -485,7 +543,7 @@ module RSpec
|
|
485
543
|
def self.run_examples(reporter)
|
486
544
|
ordering_strategy.order(filtered_examples).map do |example|
|
487
545
|
next if RSpec.world.wants_to_quit
|
488
|
-
instance = new
|
546
|
+
instance = new(example.inspect_output)
|
489
547
|
set_ivars(instance, before_context_ivars)
|
490
548
|
succeeded = example.run(instance, reporter)
|
491
549
|
RSpec.world.wants_to_quit = true if fail_fast? && !succeeded
|
@@ -510,21 +568,11 @@ module RSpec
|
|
510
568
|
RSpec.configuration.fail_fast?
|
511
569
|
end
|
512
570
|
|
513
|
-
# @private
|
514
|
-
def self.any_apply?(filters)
|
515
|
-
MetadataFilter.any_apply?(filters, metadata)
|
516
|
-
end
|
517
|
-
|
518
|
-
# @private
|
519
|
-
def self.all_apply?(filters)
|
520
|
-
MetadataFilter.all_apply?(filters, metadata)
|
521
|
-
end
|
522
|
-
|
523
571
|
# @private
|
524
572
|
def self.declaration_line_numbers
|
525
573
|
@declaration_line_numbers ||= [metadata[:line_number]] +
|
526
574
|
examples.map { |e| e.metadata[:line_number] } +
|
527
|
-
|
575
|
+
FlatMap.flat_map(children, &:declaration_line_numbers)
|
528
576
|
end
|
529
577
|
|
530
578
|
# @private
|
@@ -536,6 +584,70 @@ module RSpec
|
|
536
584
|
def self.set_ivars(instance, ivars)
|
537
585
|
ivars.each { |name, value| instance.instance_variable_set(name, value) }
|
538
586
|
end
|
587
|
+
|
588
|
+
if RUBY_VERSION.to_f < 1.9
|
589
|
+
# @private
|
590
|
+
INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
|
591
|
+
else
|
592
|
+
# @private
|
593
|
+
INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
|
594
|
+
end
|
595
|
+
|
596
|
+
# @private
|
597
|
+
def self.each_instance_variable_for_example(group)
|
598
|
+
group.instance_variables.each do |ivar|
|
599
|
+
yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
def initialize(inspect_output=nil)
|
604
|
+
@__inspect_output = inspect_output || '(no description provided)'
|
605
|
+
end
|
606
|
+
|
607
|
+
# @private
|
608
|
+
def inspect
|
609
|
+
"#<#{self.class} #{@__inspect_output}>"
|
610
|
+
end
|
611
|
+
|
612
|
+
unless method_defined?(:singleton_class) # for 1.8.7
|
613
|
+
# @private
|
614
|
+
def singleton_class
|
615
|
+
class << self; self; end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
# Raised when an RSpec API is called in the wrong scope, such as `before`
|
620
|
+
# being called from within an example rather than from within an example
|
621
|
+
# group block.
|
622
|
+
WrongScopeError = Class.new(NoMethodError)
|
623
|
+
|
624
|
+
def self.method_missing(name, *args)
|
625
|
+
if method_defined?(name)
|
626
|
+
raise WrongScopeError,
|
627
|
+
"`#{name}` is not available on an example group (e.g. a " \
|
628
|
+
"`describe` or `context` block). It is only available from " \
|
629
|
+
"within individual examples (e.g. `it` blocks) or from " \
|
630
|
+
"constructs that run in the scope of an example (e.g. " \
|
631
|
+
"`before`, `let`, etc)."
|
632
|
+
end
|
633
|
+
|
634
|
+
super
|
635
|
+
end
|
636
|
+
private_class_method :method_missing
|
637
|
+
|
638
|
+
private
|
639
|
+
|
640
|
+
def method_missing(name, *args)
|
641
|
+
if self.class.respond_to?(name)
|
642
|
+
raise WrongScopeError,
|
643
|
+
"`#{name}` is not available from within an example (e.g. an " \
|
644
|
+
"`it` block) or from constructs that run in the scope of an " \
|
645
|
+
"example (e.g. `before`, `let`, etc). It is only available " \
|
646
|
+
"on an example group (e.g. a `describe` or `context` block)."
|
647
|
+
end
|
648
|
+
|
649
|
+
super
|
650
|
+
end
|
539
651
|
end
|
540
652
|
|
541
653
|
# @private
|
@@ -545,11 +657,55 @@ module RSpec
|
|
545
657
|
{}
|
546
658
|
end
|
547
659
|
end
|
660
|
+
|
661
|
+
# Contains information about the inclusion site of a shared example group.
|
662
|
+
class SharedExampleGroupInclusionStackFrame
|
663
|
+
# @return [String] the name of the shared example group
|
664
|
+
attr_reader :shared_group_name
|
665
|
+
# @return [String] the location where the shared example was included
|
666
|
+
attr_reader :inclusion_location
|
667
|
+
|
668
|
+
def initialize(shared_group_name, inclusion_location)
|
669
|
+
@shared_group_name = shared_group_name
|
670
|
+
@inclusion_location = inclusion_location
|
671
|
+
end
|
672
|
+
|
673
|
+
# @return [String] The {#inclusion_location}, formatted for display by a formatter.
|
674
|
+
def formatted_inclusion_location
|
675
|
+
@formatted_inclusion_location ||= begin
|
676
|
+
RSpec.configuration.backtrace_formatter.backtrace_line(
|
677
|
+
inclusion_location.sub(/(:\d+):in .+$/, '\1')
|
678
|
+
)
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
# @return [String] Description of this stack frame, in the form used by
|
683
|
+
# RSpec's built-in formatters.
|
684
|
+
def description
|
685
|
+
@description ||= "Shared Example Group: #{shared_group_name.inspect} " \
|
686
|
+
"called from #{formatted_inclusion_location}"
|
687
|
+
end
|
688
|
+
|
689
|
+
# @private
|
690
|
+
def self.current_backtrace
|
691
|
+
RSpec.thread_local_metadata[:shared_example_group_inclusions].reverse
|
692
|
+
end
|
693
|
+
|
694
|
+
# @private
|
695
|
+
def self.with_frame(name, location)
|
696
|
+
current_stack = RSpec.thread_local_metadata[:shared_example_group_inclusions]
|
697
|
+
current_stack << new(name, location)
|
698
|
+
yield
|
699
|
+
ensure
|
700
|
+
current_stack.pop
|
701
|
+
end
|
702
|
+
end
|
548
703
|
end
|
549
704
|
|
550
705
|
# @private
|
551
706
|
#
|
552
|
-
# Namespace for the example group subclasses generated by top-level
|
707
|
+
# Namespace for the example group subclasses generated by top-level
|
708
|
+
# `describe`.
|
553
709
|
module ExampleGroups
|
554
710
|
extend Support::RecursiveConstMethods
|
555
711
|
|
@@ -570,16 +726,20 @@ module RSpec
|
|
570
726
|
def self.base_name_for(group)
|
571
727
|
return "Anonymous" if group.description.empty?
|
572
728
|
|
573
|
-
#
|
574
|
-
name = ' '
|
575
|
-
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/)
|
729
|
+
# Convert to CamelCase.
|
730
|
+
name = ' ' << group.description
|
731
|
+
name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
|
732
|
+
match = ::Regexp.last_match[1]
|
733
|
+
match.upcase!
|
734
|
+
match
|
735
|
+
end
|
576
736
|
|
577
|
-
name.lstrip!
|
578
|
-
name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
|
737
|
+
name.lstrip! # Remove leading whitespace
|
738
|
+
name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names
|
579
739
|
|
580
740
|
# Ruby requires first const letter to be A-Z. Use `Nested`
|
581
741
|
# as necessary to enforce that.
|
582
|
-
name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
|
742
|
+
name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)
|
583
743
|
|
584
744
|
name
|
585
745
|
end
|
@@ -597,7 +757,8 @@ module RSpec
|
|
597
757
|
def self.disambiguate(name, const_scope)
|
598
758
|
return name unless const_defined_on?(const_scope, name)
|
599
759
|
|
600
|
-
# Add a trailing number if needed to disambiguate from an existing
|
760
|
+
# Add a trailing number if needed to disambiguate from an existing
|
761
|
+
# constant.
|
601
762
|
name << "_2"
|
602
763
|
name.next! while const_defined_on?(const_scope, name)
|
603
764
|
name
|