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