Introducing TurboRspec
Testing Turbo Without Parsing HTML
Hotwire and Turbo have changed the way many Rails applications are built. They make it possible to create highly interactive interfaces while staying firmly rooted in Rails. But as I started writing more Turbo-powered applications, I noticed that testing them often felt much more cumbersome than building them. Over time, I found myself repeatedly writing the same helpers and assertions, which eventually led me to build TurboRspec.
The Problem with Testing Turbo Responses
Turbo streams and frames are elegant.
Testing them isn't always.
Many test suites end up relying on assertions like:
expect(response.body).to include("<turbo-stream")
or hand-rolled Nokogiri helpers that parse HTML and inspect attributes manually.
These approaches work, but they tend to be:
- Fragile
- Difficult to read
- Repetitive
- Hard to maintain
As applications grow, those small helpers begin to spread throughout the test suite.
Introducing TurboRspec
TurboRspec provides expressive matchers for testing Turbo Streams, Turbo Frames, broadcasts, and Stimulus attributes.
The goal wasn't to create another testing framework.
The goal was simply to make tests read more like intent and less like HTML parsing.
Instead of inspecting raw markup, tests can describe what they're expecting in a way that is easier to understand and maintain.
Getting Started
Installation is simple:
group :test do
gem "turbo_rspec"
end
In most Rails applications, no additional configuration is required.
Once installed, matchers become available automatically.
Tests That Describe Behavior
Rather than asserting on strings and DOM fragments, TurboRspec encourages tests that express behavior.
For example:
expect(response).to have_turbo_stream
.with_action(:append)
.targeting("messages")
The emphasis shifts from:
"Does this HTML contain some tag?"
to:
"Did this request append a message?"
That difference makes tests easier to read and easier to maintain.
Built for Modern Hotwire Applications
TurboRspec supports testing across multiple layers of an application, including:
- Request specs
- System tests
- Feature specs
- Turbo broadcasts
- Stimulus controllers and actions
- Minitest and RSpec
The goal is to provide a consistent testing experience regardless of where Turbo behavior is being exercised.
Less Boilerplate, More Confidence
One of the motivations behind TurboRspec was reducing the amount of custom code that accumulates in test suites.
Instead of every application maintaining its own collection of helpers and HTML parsers, teams can rely on a common set of matchers that express intent directly.
This leads to:
- Cleaner tests
- Better failure messages
- Less duplication
- More confidence when refactoring
Why I Built It
I wanted Turbo tests to feel as natural as writing Turbo itself.
I wanted to replace:
- String matching.
- Handwritten helpers.
- HTML parsing.
- Repeated boilerplate.
with expressive assertions that describe behavior.
TurboRspec is the result.
Because testing Turbo responses shouldn't require treating HTML as a string.
Tests should communicate intent and, ideally, be almost as enjoyable to write as the application code itself.