Module: RSpecTracer::Example
- Defined in:
- lib/rspec_tracer/example.rb
Overview
Builds the identity-hash payload (:example_id-keyed Hash) that
RSpec::RunnerHook attaches to every example pre-run.
Identity stability contract
example_id is the MD5 of a stable subset of the payload:
example_group (the describe block's description string),
description, full_description, shared_group (inclusion
locations with the trailing line number stripped), and
file_name. The contract, in one line: rename = new identity;
restructure = same identity.
Identity is PRESERVED when:
- blank lines or comments are added/removed around the example
- examples are reordered within a describe block (named examples only - see "Unnamed examples" below)
- a sibling describe / example in the same file is renamed
- the example body or its hooks (
before,let) are edited - the file digest still triggers the re-run
Identity CHANGES (one cold "No cache" run) when:
- the file is renamed or moved
- the
describe/it/ shared-example name is changed - the example moves to a different describe block
Unnamed examples (it { }, specify { }, example { })
An example with no description string has no stable name to hash,
and identity must be computed pre-run (for the filter decision),
before RSpec generates a matcher-derived description. The only
line-independent signal RSpec exposes pre-run is position, so an
unnamed example's identity is derived from its ordinal among the
unnamed examples of its group. (RSpec's description for an
unnamed example is the line-bearing "example at <path>:<line>"
fallback, which would otherwise leak the line number straight
back into the digest.)
For unnamed examples the contract above is amended: identity is
still PRESERVED across blank-line / comment edits, sibling
renames, and adding or removing named siblings - but it CHANGES
when the unnamed examples are reordered, or one is inserted or
removed ahead of it. Give an example an explicit description
(it 'does X' do) for a fully reorder-stable identity.
line_number / rerun_file_name / rerun_line_number stay in
the returned Hash for the reporter location columns, but are
DELIBERATELY EXCLUDED from the digest - a no-op edit that shifts
line numbers must not invalidate the cache. example_group uses
example_group.description (the user's string) rather than
example_group.name: RSpec's generated class name carries a
load-order-dependent _2 / _3 suffix when two files share a
describe name, which would otherwise flip the id across runs.
Class Method Summary collapse
Class Method Details
.from(example) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rspec_tracer/example.rb', line 66 def from(example) location = example_location(example) identity = { example_group: example.example_group.description, description: example.description, full_description: example.full_description, shared_group: example.[:shared_group_inclusion_backtrace] .map { |frame| frame.formatted_inclusion_location.sub(/:\d+\z/, '') }, file_name: location[:file_name] } identity .merge(location) .merge(example_id: Digest::MD5.hexdigest(digest_identity(example, identity).to_json)) end |