skip to content

Run Order & Filtering

The rspec command and spec_helper: random order with --seed, --bisect for order-dependent failures, metadata tags, and --only-failures. Interviewers probe chasing a failure seen only in CI.

on this pageshow

explore

questions

6

In RSpec, how do you run examples in random order, and how do you replay the exact order of a failed run with --seed?

level: middleimportance: must knowfreq 58%

answer

  1. default order is defined
  2. config.order = :random or --order rand
  3. Randomized with seed ...
  4. --seed N means --order rand:N
  5. Kernel.srand config.seed for rand

basics

~20 s

RSpec runs examples in defined order unless config.order = :random or --order rand is set. A random run ends by printing its seed, such as Randomized with seed 4821, and rspec --seed 4821 replays exactly that order.

solid answer

~40 s

Random ordering shuffles groups and examples so hidden dependencies between them surface. Turn it on with `config.order = :random` in `spec_helper.rb` or `--order rand` (alias `random`) on the command line or in `.rspec`. Each random run picks a seed and prints `Randomized with seed 4821` at the end. `rspec --seed 4821` is equivalent to `--order rand:4821` and reproduces the same order; a seed or order given on the command line or in `.rspec` wins over `config.order` in `spec_helper.rb`. The order is derived from the seed and each example's id, so rerunning a subset with the same seed keeps the survivors in the same relative order. RSpec never calls `srand` itself; add `Kernel.srand config.seed` so `rand`, `shuffle` and `sample` inside specs replay too.

code

bash · 9 lines
bash
$ rspec
...
Finished in 4.2 seconds
312 examples, 1 failure

Randomized with seed 4821

$ rspec --seed 4821          # same order, same failure
$ rspec --order defined      # does it pass without shuffling?

go deeper

for a junior

Remember that RSpec's default is defined order, that config.order = :random turns shuffling on, and that the printed seed plus --seed replays a run.

for a middle

Explain that --seed N equals --order rand:N, that command-line order wins over spec_helper, and why Kernel.srand config.seed is needed for random data.

for a senior

Use the seed from a failing CI run, confirm it reproduces locally, and explain why a subset with the same seed keeps relative order, which enables bisecting.

for a principal

Make random order the suite default so order coupling is found early, and make sure CI logs always surface the seed of a failed run.

## Defined order is the default Out of the box, rspec-core 3.13 runs example groups and examples in the order they are **defined**: files in load order, groups and examples top to bottom. That is predictable, and it is also how order dependencies hide: an example that only passes because an earlier one left a record in the database, a stubbed constant, or a memoised global behind will pass forever in defined order. ## Turning random order on Three equivalent switches exist: - `config.order = :random` in `spec_helper.rb` (suggested, commented out, in the template `rspec --init` writes); - `--order rand` or `--order random` on the command line; - the same `--order` line in `.rspec`. Other values are `defined`, `recently-modified` (most recently changed files first), and, since 3.13, the name of a custom strategy registered with `config.register_ordering`. When both are present, the command line and options files win: an `--order` or `--seed` option is forced, so a later `config.order =` in `spec_helper.rb` does not override it. ## Reading and replaying the seed Every random run chooses a seed (a random integer below 65,535 unless you give one) and prints it with the summary: ``` Randomized with seed 4821 ``` To replay that run: ``` rspec --seed 4821 ``` `--seed 4821` is documented as equivalent to `--order rand:4821`, so it also switches random ordering on even if the project normally runs in defined order. ## What gets shuffled, and what does not Random ordering is applied level by level, not across the whole suite at once: - top-level groups are ordered by the strategy; - inside a group, its own examples are ordered and run, then its nested groups, each ordered the same way; - examples from two different groups are never interleaved, so a `before(:context)` hook still runs once per group. A single group can opt out with metadata: `RSpec.describe Legacy::Importer, order: :defined do` keeps that group's examples in written order while the rest of the suite stays random. That is a documented escape hatch for code you cannot fix yet, not a fix; the dependency it hides is still there. ## How the shuffle is computed rspec-core does not use Ruby's global random generator for ordering. It sorts each list of groups or examples by a Jenkins hash of the seed concatenated with the item's id (such as `./spec/notifier_spec.rb[1:3]`). Two properties follow: 1. the same seed and the same set of examples always give the same order; 2. running a **subset** with the same seed keeps the surviving examples in the same relative order, which is what makes rerunning two ids, or `--bisect`, reproduce a failure. ## Seeding Ruby's own randomness RSpec deliberately never calls `srand`, so specs that use `rand`, `Array#shuffle` or `Array#sample` get fresh randomness each run. The template's suggestion ties that to the same seed: ```ruby RSpec.configure do |config| config.order = :random Kernel.srand config.seed end ``` With it, `--seed 4821` replays both the example order and the random data. ## Scenario: fails only in one order A suite passes on most runs and fails on a few, always with different seeds. The workflow: 1. copy the seed from the failing run's output, locally or from CI; 2. rerun with `rspec --seed <that seed>` and confirm the failure reproduces; 3. rerun the failing example alone with the same seed; if it passes, something that runs before it is leaking state; 4. hand the reproducing command to `--bisect` to find which example that is; 5. to prove the suite works when the leak is absent, run `rspec --order defined`. ## Common mistakes - Committing a fixed `config.seed = 1234`: every run then uses one order and the point of randomising is lost. - Expecting `--seed` to replay `rand` calls without `Kernel.srand config.seed`. - Reading the seed from a different run than the one that failed.

  • Why does rspec --seed 4821 spec/notifier_spec.rb:42 spec/sms_spec.rb:10 still run those two examples in the order the full run used?
    rspec-core orders items by a hash of the seed plus each item's id, not by position in a shuffled list. Removing other examples does not change the survivors' hashes, so their relative order is identical. That is what lets you reproduce a failure with only the polluting example and the victim.
  • A spec builds its data with Array#sample, and --seed 4821 does not reproduce a failure. What is missing?
    RSpec never seeds Ruby's global random generator, so `sample` draws different values each run even when the example order is replayed. Adding `Kernel.srand config.seed` in `spec_helper.rb`, as the generated template suggests, ties Ruby's `rand`, `shuffle` and `sample` to the same seed so both order and data replay.

The seed is like the shuffle number printed on a tournament draw: anyone who uses the same number gets the same pairings, and dropping a few players from the list leaves the others in the same relative order.

saying these in an interview costs you the question

  • RSpec runs examples in random order by default
  • --seed only takes effect if config.order = :random is also set
  • RSpec seeds Kernel#rand from the ordering seed automatically
  • running a subset with the same seed reshuffles the remaining examples
  • config.order in spec_helper.rb overrides --order given on the command line
open as a page

In RSpec 3.13, what does rspec --init create, and which settings in the generated spec_helper.rb are actually switched on?

level: juniorimportance: should knowfreq 45%

basics

~10 s

rspec --init writes .rspec, holding --require spec_helper, and spec/spec_helper.rb. Only three settings in that file are active; random order, :focus filtering, failure persistence and profiling sit in a commented-out =begin block.

open as a page

In RSpec, what do --only-failures, --next-failure and --profile each do, and what must spec_helper.rb set before --only-failures works?

level: middleimportance: should knowfreq 32%

basics

~10 s

--only-failures reruns just the examples that failed last time and needs config.example_status_persistence_file_path set. --next-failure also stops at the first failure in defined order. --profile reports the slowest examples and groups, ten by default.

open as a page

In RSpec, how do --tag filters and config.filter_run_when_matching :focus decide which examples run, and why can a committed fit shrink a CI run?

level: middleimportance: should knowfreq 42%

basics

~20 s

--tag slow runs only examples whose metadata has slow set, and --tag ~slow excludes them. filter_run_when_matching :focus narrows a run to focused examples only when some exist, so a fit left in a commit makes CI run just that one.

open as a page

An RSpec suite fails in CI only with --seed 4821; how does rspec --bisect narrow the failure down, and what makes it report nothing useful?

level: seniorimportance: should knowfreq 40%

basics

~20 s

rspec --seed 4821 --bisect reruns subsets in that order: it checks the failure needs other examples first, then repeatedly discards half of the passing ones, and prints a minimal reproduction command naming the victim and the example that pollutes it.

open as a page

In a Rakefile, what does RSpec::Core::RakeTask.new(:spec) define, and what happens to the rake run when examples fail?

level: middleimportance: nice to knowfreq 22%

basics

~10 s

RSpec::Core::RakeTask.new(:spec) defines a rake task that runs the rspec executable in a subprocess over spec/{,/*/}/*_spec.rb. If examples fail, fail_on_error (true by default) makes rake exit with rspec's exit status.

open as a page