class RSpec::Core::ExampleGroup

rubocop:disable Metrics/ClassLength ExampleGroup and {Example} are the main structural elements of rspec-core. Consider this example:

describe Thing do
  it "does something" do
  end
end

The object returned by `describe Thing` is a subclass of ExampleGroup. The object returned by `it “does something”` is an instance of Example, which serves as a wrapper for an instance of the ExampleGroup in which it is declared.

Example group bodies (e.g. `describe` or `context` blocks) are evaluated in the context of a new subclass of ExampleGroup. Individual examples are evaluated in the context of an instance of the specific ExampleGroup subclass to which they belong.

Besides the class methods defined here, there are other interesting macros defined in {Hooks}, {MemoizedHelpers::ClassMethods} and {SharedExampleGroup}. There are additional instance methods available to your examples defined in {MemoizedHelpers} and {Pending}.

Constants

INSTANCE_VARIABLE_TO_IGNORE

:nocov: @private

WrongScopeError

Raised when an RSpec API is called in the wrong scope, such as `before` being called from within an example rather than from within an example group block.

Public Class Methods

add_example(example) click to toggle source

Adds an example to the example group

# File lib/rspec/core/example_group.rb, line 354
def self.add_example(example)
  reset_memoized
  examples << example
end
before_context_ivars() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 478
def self.before_context_ivars
  @before_context_ivars ||= {}
end
children() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 438
def self.children
  @children ||= []
end
declaration_line_numbers() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 602
def self.declaration_line_numbers
  @declaration_line_numbers ||= [metadata[:line_number]] +
    examples.map { |e| e.metadata[:line_number] } +
    FlatMap.flat_map(children, &:declaration_line_numbers)
end
define_example_group_method(name, metadata={}) click to toggle source

@private @macro [attach] ::define_example_group_method

@!scope class
@overload $1
@overload $1(&example_group_definition)
  @param example_group_definition [Block] The definition of the example group.
@overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
  @param doc_string [String] The group's doc string.
  @param metadata [Hash] Metadata for the group.
  @param metadata_keys [Array<Symbol>] Metadata tags for the group.
    Will be transformed into hash entries with `true` values.
  @param example_group_definition [Block] The definition of the example group.

Generates a subclass of this example group which inherits
everything except the examples themselves.

@example

  RSpec.describe "something" do # << This describe method is defined in
                                # << RSpec::Core::DSL, included in the
                                # << global namespace (optional)
    before do
      do_something_before
    end

    let(:thing) { Thing.new }

    $1 "attribute (of something)" do
      # examples in the group get the before hook
      # declared above, and can access `thing`
    end
  end

@see DSL#describe

# File lib/rspec/core/example_group.rb, line 233
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

    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
    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, &example_group_block).tap do |child|
        children << child
      end

    ensure
      thread_data.delete(:in_example_group) if top_level
    end
  end

  RSpec::Core::DSL.expose_example_group_alias(name)
end
define_example_method(name, extra_options={}) click to toggle source

@private @macro [attach] ::define_example_method

@!scope class
@overload $1
@overload $1(&example_implementation)
  @param example_implementation [Block] The implementation of the example.
@overload $1(doc_string, *metadata_keys, metadata={})
  @param doc_string [String] The example's doc string.
  @param metadata [Hash] Metadata for the example.
  @param metadata_keys [Array<Symbol>] Metadata tags for the example.
    Will be transformed into hash entries with `true` values.
@overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
  @param doc_string [String] The example's doc string.
  @param metadata [Hash] Metadata for the example.
  @param metadata_keys [Array<Symbol>] Metadata tags for the example.
    Will be transformed into hash entries with `true` values.
  @param example_implementation [Block] The implementation of the example.
@yield [Example] the example object
@example
  $1 do
  end

  $1 "does something" do
  end

  $1 "does something", :slow, :uses_js do
  end

  $1 "does something", :with => 'additional metadata' do
  end

  $1 "does something" do |ex|
    # ex is the Example object that contains metadata about the example
  end
# File lib/rspec/core/example_group.rb, line 138
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)

    example = RSpec::Core::Example.new(self, desc, options, block)
    examples << example
    example
  end
end
define_nested_shared_group_method(new_name, report_label="it should behave like") click to toggle source

@private @macro [attach] ::define_nested_shared_group_method

@!scope class

@see SharedExampleGroup
# File lib/rspec/core/example_group.rb, line 304
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
delegate_to_metadata(*names) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 73
def self.delegate_to_metadata(*names)
  names.each do |name|
    idempotently_define_singleton_method(name) { metadata.fetch(name) }
  end
end
descendant_filtered_examples() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 432
def self.descendant_filtered_examples
  @descendant_filtered_examples ||= filtered_examples +
    FlatMap.flat_map(children, &:descendant_filtered_examples)
end
descendants() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 452
def self.descendants
  @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
end
description() click to toggle source

@return [String] the current example group description

# File lib/rspec/core/example_group.rb, line 82
def self.description
  description = metadata[:description]
  RSpec.configuration.format_docstrings_block.call(description)
end
each_instance_variable_for_example(group) { |ivar| ... } click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 635
def self.each_instance_variable_for_example(group)
  group.instance_variables.each do |ivar|
    yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
  end
end
ensure_example_groups_are_configured() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 467
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
examples() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 422
def self.examples
  @examples ||= []
end
filtered_examples() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 427
def self.filtered_examples
  RSpec.world.filtered_examples[self]
end
find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 366
def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
  shared_block = RSpec.world.shared_example_group_registry.find(parent_groups, name)

  unless shared_block
    raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
  end

  SharedExampleGroupInclusionStackFrame.with_frame(name, Metadata.relative_path(inclusion_location)) do
    module_exec(*args, &shared_block)
    module_exec(&customization_block) if customization_block
  end
end
for_filtered_examples(reporter, &block) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 590
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
id() click to toggle source

@return [String] the unique id of this example group. Pass

this at the command line to re-run this exact example group.
# File lib/rspec/core/example_group.rb, line 610
def self.id
  Metadata.id_from(metadata)
end
idempotently_define_singleton_method(name, &definition) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 37
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
include_context(name, *args, &block) click to toggle source

Includes shared content mapped to `name` directly in the group in which it is declared, as opposed to `it_behaves_like`, which creates a nested group. If given a block, that block is also eval'd in the current context.

@see SharedExampleGroup

# File lib/rspec/core/example_group.rb, line 330
def self.include_context(name, *args, &block)
  find_and_eval_shared("context", name, caller.first, *args, &block)
end
include_examples(name, *args, &block) click to toggle source

Includes shared content mapped to `name` directly in the group in which it is declared, as opposed to `it_behaves_like`, which creates a nested group. If given a block, that block is also eval'd in the current context.

@see SharedExampleGroup

# File lib/rspec/core/example_group.rb, line 340
def self.include_examples(name, *args, &block)
  find_and_eval_shared("examples", name, caller.first, *args, &block)
end
metadata() click to toggle source

The [Metadata](Metadata) object associated with this group. @see Metadata

# File lib/rspec/core/example_group.rb, line 48
def self.metadata
  @metadata ||= nil
end
new(inspect_output=nil) click to toggle source
Calls superclass method RSpec::Core::MemoizedHelpers.new
# File lib/rspec/core/example_group.rb, line 641
def initialize(inspect_output=nil)
  @__inspect_output = inspect_output || '(no description provided)'
  super() # no args get passed
end
next_runnable_index_for(file) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 443
def self.next_runnable_index_for(file)
  if self == ExampleGroup
    RSpec.world.num_example_groups_defined_in(file)
  else
    children.count + examples.count
  end + 1
end
ordering_strategy() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 560
      def self.ordering_strategy
        order = metadata.fetch(:order, :global)
        registry = RSpec.configuration.ordering_registry

        registry.fetch(order) do
          warn "            |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
            |         Falling back to configured global ordering.
            |         Unrecognized ordering specified at: #{location}
".gsub(/^ +\|/, '')

          registry.fetch(:global)
        end
      end
parent_groups() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 457
def self.parent_groups
  @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup }
end
remove_example(example) click to toggle source

Removes an example from the example group

# File lib/rspec/core/example_group.rb, line 360
def self.remove_example(example)
  reset_memoized
  examples.delete example
end
reset_memoized() click to toggle source

Clear memoized values when adding/removing examples @private

# File lib/rspec/core/example_group.rb, line 346
def self.reset_memoized
  @descendant_filtered_examples = nil
  @_descendants = nil
  @parent_groups = nil
  @declaration_line_numbers = nil
end
run(reporter=RSpec::Core::NullReporter) click to toggle source

Runs all the examples in this group.

# File lib/rspec/core/example_group.rb, line 536
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
    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
    run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
    reporter.example_group_finished(self)
  end
end
run_after_context_hooks(example_group_instance) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 525
def self.run_after_context_hooks(example_group_instance)
  set_ivars(example_group_instance, before_context_ivars)

  ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
    hooks.run(:after, :context, example_group_instance)
  end
ensure
  before_context_ivars.clear
end
run_before_context_hooks(example_group_instance) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 490
def self.run_before_context_hooks(example_group_instance)
  set_ivars(example_group_instance, superclass_before_context_ivars)

  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)
end
run_examples(reporter) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 576
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
set_it_up(description, *args, &example_group_block) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 397
def self.set_it_up(description, *args, &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

  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
  )
  ExampleGroups.assign_const(self)

  hooks.register_globals(self, RSpec.configuration.hooks)
  RSpec.configuration.configure_group(self)
end
set_ivars(instance, ivars) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 620
def self.set_ivars(instance, ivars)
  ivars.each { |name, value| instance.instance_variable_set(name, value) }
end
store_before_context_ivars(example_group_instance) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 483
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
subclass(parent, description, args, &example_group_block) click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 382
def self.subclass(parent, description, args, &example_group_block)
  subclass = Class.new(parent)
  subclass.set_it_up(description, *args, &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
superclass_before_context_ivars() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 502
def self.superclass_before_context_ivars
  superclass.before_context_ivars
end
superclass_metadata() click to toggle source

@private @return [Metadata] belonging to the parent of a nested {ExampleGroup}

# File lib/rspec/core/example_group.rb, line 68
def self.superclass_metadata
  @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil
end
top_level?() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 462
def self.top_level?
  superclass == ExampleGroup
end
top_level_description() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 615
def self.top_level_description
  parent_groups.last.description
end
with_replaced_metadata(meta) { || ... } click to toggle source

Temporarily replace the provided metadata. Intended primarily to allow an example group's singleton class to return the metadata of the example that it exists for. This is necessary for shared example group inclusion to work properly with singleton example groups. @private

# File lib/rspec/core/example_group.rb, line 58
def self.with_replaced_metadata(meta)
  orig_metadata = metadata
  @metadata = meta
  yield
ensure
  @metadata = orig_metadata
end

Private Class Methods

method_missing(name, *args) click to toggle source
Calls superclass method
# File lib/rspec/core/example_group.rb, line 665
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

Public Instance Methods

described_class() click to toggle source

Returns the class or module passed to the `describe` method (or alias). Returns nil if the subject is not a class or module. @example

describe Thing do
  it "does something" do
    described_class == Thing
  end
end
# File lib/rspec/core/example_group.rb, line 96
def described_class
  self.class.described_class
end
inspect() click to toggle source

@private

# File lib/rspec/core/example_group.rb, line 647
def inspect
  "#<#{self.class} #{@__inspect_output}>"
end
singleton_class() click to toggle source

:nocov: @private

# File lib/rspec/core/example_group.rb, line 654
def singleton_class
  class << self; self; end
end

Private Instance Methods

method_missing(name, *args) click to toggle source
Calls superclass method
# File lib/rspec/core/example_group.rb, line 681
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
end