name: developing-with-turbo-tests description: >- Tests Turbo Laravel features in PHPUnit or Pest. Activates when using the InteractsWithTurbo trait; simulating requests with $this->turbo(), $this->fromTurboFrame(), or $this->hotwireNative(); asserting responses with assertTurboStream(), assertNotTurboStream(), assertRedirectRecede(), assertRedirectResume(), or assertRedirectRefresh(); faking broadcasts with TurboStream::fake(), assertBroadcasted(), assertNothingWasBroadcasted(), or assertBroadcastedTimes(); writing feature tests for Turbo Stream responses; or when the user mentions testing Turbo, testing broadcasts, or Turbo test assertions.
Testing Turbo Laravel
Turbo Laravel provides the InteractsWithTurbo trait and several test response macros for asserting Turbo-specific behavior in feature tests.
Setup
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.
Simulating Turbo Requests
Turbo Stream Visits
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
Turbo Frame Requests
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
Hotwire Native Requests
Use $this->hotwireNative() to simulate a request from a Hotwire Native mobile client:
@verbatim
@endverbatim
Asserting Turbo Stream Responses
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
Asserting Hotwire Native Redirects
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
Testing Broadcasts
Faking Broadcasts
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