class RSpec::Core::ExampleGroup
your examples defined in {MemoizedHelpers} and {Pending}.
{SharedExampleGroup}. There are additional instance methods available to
defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
Besides the class methods defined here, there are other interesting macros
subclass to which they belong.
evaluated in the context of an instance of the specific ExampleGroup
in the context of a new subclass of ExampleGroup. Individual examples are
Example group bodies (e.g. ‘describe` or `context` blocks) are evaluated
is declared.
which serves as a wrapper for an instance of the ExampleGroup in which it
The object returned by `it “does something”` is an instance of Example,
The object returned by `describe Thing` is a subclass of ExampleGroup.
end
end
it “does something” do
RSpec.describe Thing do
rspec-core. Consider this example:
ExampleGroup and {Example} are the main structural elements of
def self.add_example(example)
def self.add_example(example) reset_memoized examples << example end
def self.before_context_ivars
- Private: -
def self.before_context_ivars @before_context_ivars ||= {} end
def self.children
- Private: -
def self.children @children ||= [] end
def self.currently_executing_a_context_hook?
Returns true if a `before(:context)` or `after(:context)`
def self.currently_executing_a_context_hook? @currently_executing_a_context_hook end
def self.declaration_locations
- Private: -
def self.declaration_locations @declaration_locations ||= [Metadata.location_tuple_from(metadata)] + examples.map { |e| Metadata.location_tuple_from(e.metadata) } + FlatMap.flat_map(children, &:declaration_locations) end
def self.define_example_group_method(name, metadata={})
- See: DSL#describe -
Parameters:
-
example_group_definition
(Block
) -- The definition of the example group. -
metadata
(Array
) -- Metadata for the group., Hash -
doc_string
(String
) -- The group's doc string. -
example_group_definition
(Block
) -- The definition of the example group.
Overloads:
-
$1(doc_string, *metadata, &example_implementation)
-
$1(&example_group_definition)
-
$1
Other tags:
- Private: -
def self.define_example_group_method(name, metadata={}) idempotently_define_singleton_method(name) do |*args, &example_group_block| thread_data = RSpec::Support.thread_local_data top_level = self == ExampleGroup registration_collection = if top_level if thread_data[:in_example_group] raise "Creating an isolated context from within a context is " \ "not allowed. Change `RSpec.#{name}` to `#{name}` or " \ "move this to a top-level scope." end thread_data[:in_example_group] = true RSpec.world.example_groups else children end begin description = args.shift combined_metadata = metadata.dup combined_metadata.merge!(args.pop) if args.last.is_a? Hash args << combined_metadata subclass(self, description, args, registration_collection, &example_group_block) ensure thread_data.delete(:in_example_group) if top_level end end RSpec::Core::DSL.expose_example_group_alias(name) end
def self.define_example_method(name, extra_options={})
- Yield: - the example object
Parameters:
-
example_implementation
(Block
) -- The implementation of the example. -
metadata
(Array
) -- Metadata for the example., Hash -
doc_string
(String
) -- The example's doc string. -
metadata
(Array
) -- Metadata for the example., Hash -
doc_string
(String
) -- The example's doc string. -
example_implementation
(Block
) -- The implementation of the example.
Overloads:
-
$1(doc_string, *metadata, &example_implementation)
-
$1(doc_string, *metadata)
-
$1(&example_implementation)
-
$1
Other tags:
- Private: -
def self.define_example_method(name, extra_options={}) idempotently_define_singleton_method(name) do |*all_args, &block| desc, *args = *all_args options = Metadata.build_hash_from(args) options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block options.update(extra_options) RSpec::Core::Example.new(self, desc, options, block) end end
def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
- See: SharedExampleGroup -
Other tags:
- Private: -
def self.define_nested_shared_group_method(new_name, report_label="it should behave like") idempotently_define_singleton_method(new_name) do |name, *args, &customization_block| # Pass :caller so the :location metadata is set properly. # Otherwise, it'll be set to the next line because that's # the block's source_location. group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block) end group.metadata[:shared_group_name] = name group end end
def self.delegate_to_metadata(*names)
- Private: -
def self.delegate_to_metadata(*names) names.each do |name| idempotently_define_singleton_method(name) { metadata.fetch(name) } end end
def self.descendant_filtered_examples
- Private: -
def self.descendant_filtered_examples @descendant_filtered_examples ||= filtered_examples + FlatMap.flat_map(children, &:descendant_filtered_examples) end
def self.descendants
- Private: -
def self.descendants @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants) end
def self.description
-
(String)
- the current example group description
def self.description description = metadata[:description] RSpec.configuration.format_docstrings_block.call(description) end
def self.each_instance_variable_for_example(group)
- Private: -
def self.each_instance_variable_for_example(group) group.instance_variables.each do |ivar| yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE end end
def self.ensure_example_groups_are_configured
- Private: -
def self.ensure_example_groups_are_configured unless defined?(@@example_groups_configured) RSpec.configuration.configure_mock_framework RSpec.configuration.configure_expectation_framework # rubocop:disable Style/ClassVars @@example_groups_configured = true # rubocop:enable Style/ClassVars end end
def self.examples
- Private: -
def self.examples @examples ||= [] end
def self.filtered_examples
- Private: -
def self.filtered_examples RSpec.world.filtered_examples[self] end
def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
- Private: -
def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name) unless shared_module raise ArgumentError, "Could not find shared #{label} #{name.inspect}" end shared_module.include_in( self, Metadata.relative_path(inclusion_location), args, customization_block ) end
def self.for_filtered_examples(reporter, &block)
- Private: -
def self.for_filtered_examples(reporter, &block) filtered_examples.each(&block) children.each do |child| reporter.example_group_started(child) child.for_filtered_examples(reporter, &block) reporter.example_group_finished(child) end false end
def self.id
-
(String)
- the unique id of this example group. Pass
def self.id Metadata.id_from(metadata) end
def self.idempotently_define_singleton_method(name, &definition)
- Private: -
def self.idempotently_define_singleton_method(name, &definition) (class << self; self; end).module_exec do remove_method(name) if method_defined?(name) && instance_method(name).owner == self define_method(name, &definition) end end
def self.include_context(name, *args, &block)
- See: SharedExampleGroup -
def self.include_context(name, *args, &block) find_and_eval_shared("context", name, caller.first, *args, &block) end
def self.include_examples(name, *args, &block)
- See: SharedExampleGroup -
def self.include_examples(name, *args, &block) find_and_eval_shared("examples", name, caller.first, *args, &block) end
def self.metadata
- See: Metadata -
def self.metadata @metadata ||= nil end
def self.method_missing(name, *args)
def self.method_missing(name, *args) if method_defined?(name) raise WrongScopeError, "`#{name}` is not available on an example group (e.g. a " \ "`describe` or `context` block). It is only available from " \ "within individual examples (e.g. `it` blocks) or from " \ "constructs that run in the scope of an example (e.g. " \ "`before`, `let`, etc)." end super end
def self.next_runnable_index_for(file)
- Private: -
def self.next_runnable_index_for(file) if self == ExampleGroup # We add 1 so the ids start at 1 instead of 0. This is # necessary for this branch (but not for the other one) # because we register examples and groups with the # `children` and `examples` collection BEFORE this # method is called as part of metadata hash creation, # but the example group is recorded with # `RSpec.world.example_group_counts_by_spec_file` AFTER # the metadata hash is created and the group is returned # to the caller. RSpec.world.num_example_groups_defined_in(file) + 1 else children.count + examples.count end end
def self.ordering_strategy
- Private: -
def self.ordering_strategy order = metadata.fetch(:order, :global) registry = RSpec.configuration.ordering_registry registry.fetch(order) do warn <<-WARNING.gsub(/^ +\|/, '') |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata. | Falling back to configured global ordering. | Unrecognized ordering specified at: #{location} WARNING registry.fetch(:global) end end
def self.parent_groups
def self.parent_groups @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup } end
def self.remove_example(example)
def self.remove_example(example) reset_memoized examples.delete example end
def self.reset_memoized
- Private: -
def self.reset_memoized @descendant_filtered_examples = nil @_descendants = nil @parent_groups = nil @declaration_locations = nil end
def self.run(reporter=RSpec::Core::NullReporter)
def self.run(reporter=RSpec::Core::NullReporter) return if RSpec.world.wants_to_quit reporter.example_group_started(self) should_run_context_hooks = descendant_filtered_examples.any? begin RSpec.current_scope = :before_context_hook run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks result_for_this_group = run_examples(reporter) results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all? result_for_this_group && results_for_descendants rescue Pending::SkipDeclaredInExample => ex for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) } true rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met? false ensure RSpec.current_scope = :after_context_hook run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks reporter.example_group_finished(self) end end
def self.run_after_context_hooks(example_group_instance)
- Private: -
def self.run_after_context_hooks(example_group_instance) set_ivars(example_group_instance, before_context_ivars) @currently_executing_a_context_hook = true ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do hooks.run(:after, :context, example_group_instance) end ensure before_context_ivars.clear @currently_executing_a_context_hook = false end
def self.run_before_context_hooks(example_group_instance)
- Private: -
def self.run_before_context_hooks(example_group_instance) set_ivars(example_group_instance, superclass_before_context_ivars) @currently_executing_a_context_hook = true ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do hooks.run(:before, :context, example_group_instance) end ensure store_before_context_ivars(example_group_instance) @currently_executing_a_context_hook = false end
def self.run_examples(reporter)
- Private: -
def self.run_examples(reporter) ordering_strategy.order(filtered_examples).map do |example| next if RSpec.world.wants_to_quit instance = new(example.inspect_output) set_ivars(instance, before_context_ivars) succeeded = example.run(instance, reporter) if !succeeded && reporter.fail_fast_limit_met? RSpec.world.wants_to_quit = true end succeeded end.all? end
def self.set_it_up(description, args, registration_collection, &example_group_block)
- Private: -
def self.set_it_up(description, args, registration_collection, &example_group_block) # Ruby 1.9 has a bug that can lead to infinite recursion and a # SystemStackError if you include a module in a superclass after # including it in a subclass: https://gist.github.com/845896 # To prevent this, we must include any modules in # RSpec::Core::ExampleGroup before users create example groups and have # a chance to include the same module in a subclass of # RSpec::Core::ExampleGroup. So we need to configure example groups # here. ensure_example_groups_are_configured # Register the example with the group before creating the metadata hash. # This is necessary since creating the metadata hash triggers # `when_first_matching_example_defined` callbacks, in which users can # load RSpec support code which defines hooks. For that to work, the # examples and example groups must be registered at the time the # support code is called or be defined afterwards. # Begin defined beforehand but registered afterwards causes hooks to # not be applied where they should. registration_collection << self @user_metadata = Metadata.build_hash_from(args) @metadata = Metadata::ExampleGroupHash.create( superclass_metadata, @user_metadata, superclass.method(:next_runnable_index_for), description, *args, &example_group_block ) config = RSpec.configuration config.apply_derived_metadata_to(@metadata) ExampleGroups.assign_const(self) @currently_executing_a_context_hook = false config.configure_group(self) end
def self.set_ivars(instance, ivars)
- Private: -
def self.set_ivars(instance, ivars) ivars.each { |name, value| instance.instance_variable_set(name, value) } end
def self.store_before_context_ivars(example_group_instance)
- Private: -
def self.store_before_context_ivars(example_group_instance) each_instance_variable_for_example(example_group_instance) do |ivar| before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar) end end
def self.subclass(parent, description, args, registration_collection, &example_group_block)
- Private: -
def self.subclass(parent, description, args, registration_collection, &example_group_block) subclass = Class.new(parent) subclass.set_it_up(description, args, registration_collection, &example_group_block) subclass.module_exec(&example_group_block) if example_group_block # The LetDefinitions module must be included _after_ other modules # to ensure that it takes precedence when there are name collisions. # Thus, we delay including it until after the example group block # has been eval'd. MemoizedHelpers.define_helpers_on(subclass) subclass end
def self.superclass_before_context_ivars
- Private: -
def self.superclass_before_context_ivars superclass.before_context_ivars end
def self.superclass_before_context_ivars
- Private: -
def self.superclass_before_context_ivars if superclass.respond_to?(:before_context_ivars) superclass.before_context_ivars else # `self` must be the singleton class of an ExampleGroup instance. # On 1.8.7, the superclass of a singleton class of an instance of A # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor # is A, so we can mirror 1.8.7's behavior here. Note that we have to # search for the first that responds to `before_context_ivars` # in case a module has been included in the singleton class. ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars end end
def self.superclass_metadata
-
(Metadata)
- belonging to the parent of a nested {ExampleGroup}
Other tags:
- Private: -
def self.superclass_metadata @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil end
def self.top_level?
- Private: -
def self.top_level? superclass == ExampleGroup end
def self.top_level_description
- Private: -
def self.top_level_description parent_groups.last.description end
def self.traverse_tree_until(&block)
- Private: -
def self.traverse_tree_until(&block) return if yield self children.each do |child| child.traverse_tree_until(&block) end end
def self.update_inherited_metadata(updates)
- Private: -
def self.update_inherited_metadata(updates) metadata.update(updates) do |key, existing_group_value, new_inherited_value| @user_metadata.key?(key) ? existing_group_value : new_inherited_value end RSpec.configuration.configure_group(self) examples.each { |ex| ex.update_inherited_metadata(updates) } children.each { |group| group.update_inherited_metadata(updates) } end
def self.with_replaced_metadata(meta)
- Private: -
def self.with_replaced_metadata(meta) orig_metadata = metadata @metadata = meta yield ensure @metadata = orig_metadata end
def described_class
end
end
described_class == Thing
it "does something" do
RSpec.describe Thing do
@example
Returns nil if the subject is not a class or module.
Returns the class or module passed to the `describe` method (or alias).
def described_class self.class.described_class end
def initialize(inspect_output=nil)
- Private: -
def initialize(inspect_output=nil) @__inspect_output = inspect_output || '(no description provided)' super() # no args get passed end
def inspect
- Private: -
def inspect "#<#{self.class} #{@__inspect_output}>" end
def method_missing(name, *args)
def method_missing(name, *args) if self.class.respond_to?(name) raise WrongScopeError, "`#{name}` is not available from within an example (e.g. an " \ "`it` block) or from constructs that run in the scope of an " \ "example (e.g. `before`, `let`, etc). It is only available " \ "on an example group (e.g. a `describe` or `context` block)." end super(name, *args) end
def singleton_class
- Private: -
def singleton_class class << self; self; end end