Basics of testing out Turbo-specific aspects of web applications. Activate when working on projects that utilize Turbo for testing specific Turbo Frames, Turbo Streams, or Turbo Native behavior.
Turbo Laravel provides the InteractsWithTurbo trait and several test response macros for asserting Turbo-specific behavior in feature tests.
Add the InteractsWithTurbo trait to your test class (or base TestCase):
@verbatim
class PostTest extends TestCase { use InteractsWithTurbo; }
@endverbatim
Note: The turbo-laravel.queue config is automatically set to false during testing so broadcasts are processed synchronously.
Use $this->turbo() to simulate a request that accepts Turbo Stream responses (sets the appropriate Accept header):
@verbatim
$this->turbo()->put(route('posts.update', $post), ['title' => 'Updated']) ->assertTurboStream();
$this->turbo()->delete(route('posts.destroy', $post)) ->assertTurboStream();
@endverbatim
Use $this->fromTurboFrame() to simulate a request from a specific Turbo Frame (sets the Turbo-Frame header):
@verbatim
$this->fromTurboFrame(dom_id($post, 'create_comment')) ->post(route('posts.comments.store', $post), ['content' => 'Hello']) ->assertOk();
@endverbatim
Use $this->hotwireNative() to simulate a request from a Hotwire Native mobile client:
@verbatim
@endverbatim
assertTurboStream()Assert the response is a Turbo Stream. Optionally pass a callback to inspect individual streams:
@verbatim
// With callback for detailed assertions $this->turbo()->post(route('posts.store'), ['title' => 'Test']) ->assertTurboStream(fn ($streams) => $streams ->has(2) // Assert exactly 2 stream elements ->hasTurboStream(fn ($s) => $s ->where('target', 'posts') ->where('action', 'append') ->see('Test') ) ->hasTurboStream(fn ($s) => $s ->where('target', 'post_count') ->where('action', 'update') ) );
@endverbatim
assertNotTurboStream()Assert the response is NOT a Turbo Stream:
@verbatim
@endverbatim
For Hotwire Native clients, assert specific redirect path behaviors with optional flash data:
@verbatim
// Assert a "resume" redirect (stay on the current screen) $this->hotwireNative()->put(route('settings.update'), ['name' => 'New']) ->assertRedirectResume(['status' => __('Settings updated.')]);
// Assert a "refresh" redirect (reload the current screen) $this->hotwireNative()->post(route('posts.store'), ['title' => 'Test']) ->assertRedirectRefresh(['status' => __('Post created.')]);
@endverbatim
Use TurboStream::fake() to capture broadcasts without actually sending them:
@verbatim
public function test_creating_post_broadcasts() { TurboStream::fake();
$post = Post::create(['title' => 'Test']);
TurboStream::assertBroadcasted(fn ($broadcast) =>
$broadcast->target === 'posts' && $broadcast->action === 'append'
);
}
@endverbatim
assertNothingWasBroadcasted()Assert no broadcasts were sent:
@verbatim
// ... perform actions that should NOT broadcast ...
TurboStream::assertNothingWasBroadcasted();
@endverbatim
assertBroadcastedTimes()Assert a broadcast matching a condition was sent a specific number of times:
@verbatim
Post::create(['title' => 'First']); Post::create(['title' => 'Second']);
TurboStream::assertBroadcastedTimes( fn ($broadcast) => $broadcast->action === 'append', times: 2, );
@endverbatim