RSpec: Marking sections in long examples

Posted . Visible to the public.

RSpec examples can get quite long, especially in feature specs. This makes them hard to read & understand. Also, when executing them, it may take seconds to see any progress.

To improve this, I have successfully been using a little "step" helper in my tests. It marks semantic sections, structuring an example while improving documentation. When the test runs, each step prints its message (or a dot, depending on your formatter).

# spec/support/step_helper.rb
module StepHelper

  # Use this helper to label groups of related actions in long specs.
  # This helps understanding a spec, as well as tracking progress during execution.
  #
  # Example:
  # it 'offers a login' do
  #   # Test setup
  #
  #   STEP 'Attempt login with wrong credentials'
  #   # ...
  #
  #   STEP 'Successful login'
  #   # ...
  # end
  def STEP(label)
    formatters = RSpec.configuration.formatters.map(&:class)
    example = RSpec.current_example
    hierarchy = example.example_group.parent_groups
    indent = '  ' * hierarchy.count

    if formatters.include? RSpec::Core::Formatters::DocumentationFormatter
      puts "#{indent}⮦ #{label}"
    elsif formatters.include? RSpec::Core::Formatters::ProgressFormatter
      print '.'
    else
      raise "Unexpected RSpec formatters: #{formatters.map(&:name)}"
    end
  end

end

RSpec.configure do |config|
  config.include StepHelper
end

Usage example

  scenario 'The list of advisories is paginated' do
    create :npm_advisory, title: 'oldest', created_at: '2019-05-01'
    create :npm_advisory, title: 'middle', created_at: '2019-06-01'
    create :npm_advisory, title: 'latest', created_at: '2019-07-01'
    elements_per_page DepReporter::Advisory, 2

    STEP 'visit the first page of advisories'
    visit advisories_path
    expect(page).to have_content('latest')
      .and have_content('middle')
      .and have_no_content('oldest')

    STEP 'navigate to the second page'
    click_link '2'
    expect(page).to have_content('oldest')
      .and have_no_content('latest')
      .and have_no_content('middle')
  end
Profile picture of Dominik Schöler
Dominik Schöler
Last edit
Dominik Schöler
License
Source code in this card is licensed under the MIT License.
Posted by Dominik Schöler to makandra dev (2025-08-22 13:56)