class RSpec::Core::Example

Wrapper for an instance of a subclass of {ExampleGroup}. An instance of ‘RSpec::Core::Example` is returned by example definition methods such as {ExampleGroup.it it} and is yielded to the {ExampleGroup.it it}, {Hooks#before before}, {Hooks#after after}, {Hooks#around around}, {MemoizedHelpers::ClassMethods#let let} and {MemoizedHelpers::ClassMethods#subject subject} blocks.

This allows us to provide rich metadata about each individual example without adding tons of methods directly to the ExampleGroup that users may inadvertently redefine.

Useful for configuring logging and/or taking some action based on the state of an example’s metadata.

@example

RSpec.configure do |config|
  config.before do |example|
    log example.description
  end

  config.after do |example|
    log example.description
  end

  config.around do |example|
    log example.description
    example.run
  end
end

shared_examples "auditable" do
  it "does something" do
    log "#{example.full_description}: #{auditable.inspect}"
    auditable.should do_something
  end
end

@see ExampleGroup @note Example blocks are evaluated in the context of an instance

of an `ExampleGroup`, not in the context of an instance of `Example`.

Constants

AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt

:nocov: For some reason, rescuing ‘Support::AllExceptionsExceptOnesWeMustNotRescue` in place of `Exception` above can cause the exit status to be the wrong thing. I have no idea why. See: github.com/rspec/rspec-core/pull/2063#discussion_r38284978 @private

Attributes

@attr @private

@attr_reader @private

Returns the example_group_instance that provides the context for running this example.

@attr_reader

Returns the first exception raised in the context of running this example (nil if no exception is raised).

@attr_reader

Returns the metadata object associated with this example.

@return [RSpec::Core::Reporter] the current reporter for the example

Public Class Methods

@private

Used to define methods that delegate to this example’s metadata.

# File rspec-core/lib/rspec/core/example.rb, line 48
def self.delegate_to_metadata(key)
  define_method(key) { @metadata[key] }
end

Creates a new instance of Example. @param example_group_class [Class] the subclass of ExampleGroup in which

this Example is declared

@param description [String] the String passed to the ‘it` method (or

alias)

@param user_metadata [Hash] additional args passed to ‘it` to be used as

metadata

@param example_block [Proc] the block of code that represents the

example

@api private

# File rspec-core/lib/rspec/core/example.rb, line 186
def initialize(example_group_class, description, user_metadata, example_block=nil)
  @example_group_class = example_group_class
  @example_block       = example_block

  # 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.
  example_group_class.examples << self

  @metadata = Metadata::ExampleHash.create(
    @example_group_class.metadata, user_metadata,
    example_group_class.method(:next_runnable_index_for),
    description, example_block
  )

  config = RSpec.configuration
  config.apply_derived_metadata_to(@metadata)

  # This should perhaps be done in `Metadata::ExampleHash.create`,
  # but the logic there has no knowledge of `RSpec.world` and we
  # want to keep it that way. It's easier to just assign it here.
  @metadata[:last_run_status] = config.last_run_statuses[id]

  @example_group_instance = @exception = nil
  @clock = RSpec::Core::Time
  @reporter = RSpec::Core::NullReporter
end

@private

# File rspec-core/lib/rspec/core/example.rb, line 122
def self.parse_id(id)
  # http://rubular.com/r/OMZSAPcAfn
  id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures
end

Public Instance Methods

Returns the string submitted to ‘example` or its aliases (e.g. `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to do_something }`) it returns the message generated by the matcher if there is one, otherwise returns a message including the location of the example.

# File rspec-core/lib/rspec/core/example.rb, line 76
def description
  description = if metadata[:description].to_s.empty?
                  location_description
                else
                  metadata[:description]
                end

  RSpec.configuration.format_docstrings_block.call(description)
end

@private

The exception that will be displayed to the user – either the failure of the example or the ‘pending_exception` if the example is pending.

# File rspec-core/lib/rspec/core/example.rb, line 388
def display_exception
  @exception || execution_result.pending_exception
end

@private

Assigns the exception that will be displayed to the user – either the failure of the example or the ‘pending_exception` if the example is pending.

# File rspec-core/lib/rspec/core/example.rb, line 396
def display_exception=(ex)
  if pending? && !(Pending::PendingExampleFixedError === ex)
    @exception = nil
    execution_result.pending_fixed = false
    execution_result.pending_exception = ex
  else
    @exception = ex
  end
end

Duplicates the example and overrides metadata with the provided hash.

@param metadata_overrides [Hash] the hash to override the example metadata @return [Example] a duplicate of the example with modified metadata

# File rspec-core/lib/rspec/core/example.rb, line 132
def duplicate_with(metadata_overrides={})
  new_metadata = metadata.clone.merge(metadata_overrides)

  RSpec::Core::Metadata::RESERVED_KEYS.each do |reserved_key|
    new_metadata.delete reserved_key
  end

  # don't clone the example group because the new example
  # must belong to the same example group (not a clone).
  #
  # block is nil in new_metadata so we have to get it from metadata.
  Example.new(example_group, description.clone,
              new_metadata, metadata[:block])
end

Returns the example group class that provides the context for running this example.

# File rspec-core/lib/rspec/core/example.rb, line 230
def example_group
  @example_group_class
end

@private

Used internally to set an exception and fail without actually executing the example when an exception is raised in before(:context).

# File rspec-core/lib/rspec/core/example.rb, line 439
def fail_with_exception(reporter, exception)
  start(reporter)
  set_exception(exception)
  finish(reporter)
end

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

this at the command line to re-run this exact example.
# File rspec-core/lib/rspec/core/example.rb, line 117
def id
  @id ||= Metadata.id_from(metadata)
end

Provide a human-readable representation of this class

# File rspec-core/lib/rspec/core/example.rb, line 220
def inspect
  "#<#{self.class.name} #{description.inspect}>"
end
Also aliased as: to_s

Returns a description of the example that always includes the location.

# File rspec-core/lib/rspec/core/example.rb, line 87
def inspect_output
  inspect_output = "\"#{description}\""
  unless metadata[:description].to_s.empty?
    inspect_output += " (#{location})"
  end
  inspect_output
end

@private

# File rspec-core/lib/rspec/core/example.rb, line 456
def instance_exec(*args, &block)
  @example_group_instance.instance_exec(*args, &block)
end

Returns the location-based argument that can be passed to the ‘rspec` command to rerun this example.

# File rspec-core/lib/rspec/core/example.rb, line 96
def location_rerun_argument
  @location_rerun_argument ||= begin
    loaded_spec_files = RSpec.configuration.loaded_spec_files

    Metadata.ascending(metadata) do |meta|
      break meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
    end
  end
end
# File rspec-core/lib/rspec/core/example.rb, line 234
def pending?
  !!pending
end

Returns the location-based argument that can be passed to the ‘rspec` command to rerun this example.

@deprecated Use {#location_rerun_argument} instead. @note If there are multiple examples identified by this location, they will use {#id}

to rerun instead, but this method will still return the location (that's why it is deprecated!).
# File rspec-core/lib/rspec/core/example.rb, line 111
def rerun_argument
  location_rerun_argument
end

@api private instance_execs the block passed to the constructor in the context of the instance of {ExampleGroup}. @param example_group_instance the instance of an ExampleGroup subclass

# File rspec-core/lib/rspec/core/example.rb, line 246
def run(example_group_instance, reporter)
  @example_group_instance = example_group_instance
  @reporter = reporter
  RSpec.configuration.configure_example(self, hooks)
  RSpec.current_example = self

  start(reporter)
  Pending.mark_pending!(self, pending) if pending?

  begin
    if skipped?
      Pending.mark_pending! self, skip
    elsif !RSpec.configuration.dry_run?
      with_around_and_singleton_context_hooks do
        begin
          run_before_example
          RSpec.current_scope = :example
          @example_group_instance.instance_exec(self, &@example_block)

          if pending?
            Pending.mark_fixed! self

            raise Pending::PendingExampleFixedError,
                  'Expected example to fail since it is pending, but it passed.',
                  [location]
          end
        rescue Pending::SkipDeclaredInExample => _
          # The "=> _" is normally useless but on JRuby it is a workaround
          # for a bug that prevents us from getting backtraces:
          # https://github.com/jruby/jruby/issues/4467
          #
          # no-op, required metadata has already been set by the `skip`
          # method.
        rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e
          set_exception(e)
        ensure
          RSpec.current_scope = :after_example_hook
          run_after_example
        end
      end
    end
  rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
    set_exception(e)
  ensure
    @example_group_instance = nil # if you love something... let it go
  end

  finish(reporter)
ensure
  execution_result.ensure_timing_set(clock)
  RSpec.current_example = nil
end

@private

Used to set the exception when ‘aggregate_failures` fails.

# File rspec-core/lib/rspec/core/example.rb, line 425
def set_aggregate_failures_exception(exception)
  return set_exception(exception) unless display_exception

  exception = RSpec::Core::MultipleExceptionError::InterfaceTag.for(exception)
  exception.add display_exception
  self.display_exception = exception
end

@private

Used internally to set an exception in an after hook, which captures the exception but doesn’t raise it.

# File rspec-core/lib/rspec/core/example.rb, line 412
def set_exception(exception)
  return self.display_exception = exception unless display_exception

  unless RSpec::Core::MultipleExceptionError === display_exception
    self.display_exception = RSpec::Core::MultipleExceptionError.new(display_exception)
  end

  display_exception.add exception
end

@private

Used internally to skip without actually executing the example when skip is used in before(:context).

# File rspec-core/lib/rspec/core/example.rb, line 449
def skip_with_exception(reporter, exception)
  start(reporter)
  Pending.mark_skipped! self, exception.argument
  finish(reporter)
end
# File rspec-core/lib/rspec/core/example.rb, line 238
def skipped?
  !!skip
end
Alias for: inspect

@private

# File rspec-core/lib/rspec/core/example.rb, line 148
def update_inherited_metadata(updates)
  metadata.update(updates) do |_key, existing_example_value, _new_inherited_value|
    existing_example_value
  end
end