StimulusSpec

CI Gem Version Gem Downloads Ruby codecov

Drop-in RSpec matchers for hotwired/stimulus-rails — stop hand-rolling data-controller assertions and test your Stimulus wiring with expressive, purpose-built matchers.

  • Request/controller specshave_stimulus_controller, have_stimulus_action, have_stimulus_target
  • Auto-included — zero setup required when stimulus-rails is in your bundle
  • Configurable — disable auto-include when you need manual control

Companion gem to turbo_rspec — together they cover the full Hotwire testing stack.

Table of Contents

Installation

Add to your application's Gemfile:

group :test do
  gem "stimulus_spec"
end

Back to top

Setup

Rails + stimulus-rails (automatic)

No setup needed. When stimulus-rails is in your bundle, StimulusSpec::Matchers is automatically included in type: :request, :controller, :system, and :feature example groups.

Manual include

For non-Rails projects or custom contexts, include the matchers explicitly:

# spec/spec_helper.rb
RSpec.configure do |config|
  config.include StimulusSpec::Matchers
end

Configuration

# spec/support/stimulus_spec.rb
StimulusSpec.configure do |config|
  config.auto_include = false  # disable automatic inclusion
end

Back to top

Matchers

have_stimulus_controller

Assert that rendered HTML contains a data-controller attribute with the given controller name.

expect(response).to have_stimulus_controller("hello")

# Works with multiple controllers on a single element
expect(response).to have_stimulus_controller("clipboard")

# Negation
expect(response).not_to have_stimulus_controller("missing")

Uses space-separated token matching (~=), so it works correctly when multiple controllers are declared on a single element and won't partially match.

have_stimulus_action

Assert that rendered HTML contains a data-action attribute with the given action descriptor.

# Full descriptor
expect(response).to have_stimulus_action("click->hello#greet")

# Shorthand — matches any event
expect(response).to have_stimulus_action("hello#greet")

# Negation
expect(response).not_to have_stimulus_action("hello#disconnect")

have_stimulus_target

Assert that rendered HTML contains a data-{controller}-target attribute with the given target name.

expect(response).to have_stimulus_target("hello", "name")
expect(response).to have_stimulus_target("hello", "output")

# Negation
expect(response).not_to have_stimulus_target("hello", "missing")

Back to top

Example

RSpec.describe "Search", type: :request do
  describe "GET /search" do
    it "wires up the search controller" do
      get search_path

      expect(response).to have_stimulus_controller("search")
      expect(response).to have_stimulus_action("input->search#query")
      expect(response).to have_stimulus_target("search", "input")
    end
  end
end

Back to top

Relationship to turbo_rspec

turbo_rspec includes basic Stimulus matchers (have_stimulus_controller, have_stimulus_action, have_stimulus_target). stimulus_spec goes deeper with value, class, and outlet matchers, plus richer failure messages and Stimulus-specific configuration. If you only need basic controller/action/target assertions alongside your Turbo matchers, turbo_rspec has you covered. If you want comprehensive Stimulus testing, use stimulus_spec.

Both gems can coexist — they use separate namespaces and won't conflict.

Back to top

Contributing

Bug reports and pull requests are welcome on GitHub. See CONTRIBUTING.md for setup instructions, branch conventions, CHANGELOG requirements, and the PR checklist.

License

The gem is available as open source under the MIT License.

Back to top