Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Testbench Browser Kit Laravel Package

orchestra/testbench-browser-kit

BrowserKit Testbench component helps package developers write Laravel tests using laravel/browser-kit-testing. Swap your base TestCase for Orchestra\Testbench\BrowserKit\TestCase and use the fluent visit, see, and form APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

Reach for this package when your package (not a full app) ships routes, views, controllers or form-driven flows, and you want to exercise them the legacy BrowserKit way: visit(), click(), type(), press(). It is a thin bridge that swaps Testbench's base TestCase for one that includes laravel/browser-kit-testing's MakesHttpRequests + InteractsWithPages traits, so a package skeleton gets a real HTTP kernel, a session, and a DomCrawler to poke at.

Install (dev only), matching your Laravel major — not your Testbench major:

Laravel testbench-browser-kit
6.x 4.x
7.x 5.x
8.x 6.x
9.x 7.x
10.x 8.x
11.x 9.x
12.x 10.x
composer require --dev "orchestra/testbench-browser-kit"

Look first at: the README "Usages" section (it is the whole API surface, inherited from Laravel's BrowserKit docs), then Orchestra\Testbench\BrowserKit\TestCase — it extends Orchestra\Testbench\TestCase, so every Testbench hook (getPackageProviders, getEnvironmentSetUp, defineRoutes, RefreshDatabase, …) still applies. There is nothing else to the package.

Minimal working setup — one base TestCase for the whole suite:

<?php

namespace Acme\Blog\Tests;

use Orchestra\Testbench\BrowserKit\TestCase as BaseTestCase;
use Illuminate\Routing\Router;

abstract class TestCase extends BaseTestCase
{
    // Required: no default, you get "no host" style failures without it.
    public $baseUrl = 'http://localhost';

    protected function getPackageProviders($app)
    {
        return [\Acme\Blog\BlogServiceProvider::class];
    }

    protected function getEnvironmentSetUp($app)
    {
        $app['config']->set('view.paths', [__DIR__.'/fixtures/views']);
        $app['config']->set('app.key', 'base64:'.base64_encode(random_bytes(32)));
    }

    protected function defineRoutes($router)   // Testbench 8+
    {
        $router->middleware('web')->group(function (Router $router) {
            $router->get('/', 'Acme\Blog\Http\Controllers\PostController@index')
                   ->name('posts.index');
        });

        require __DIR__.'/fixtures/routes.php'; // if you keep them in a file
    }
}

First use case — prove a package route renders package views:

public function test_index_lists_posts()
{
    $this->visitRoute('posts.index')
         ->assertResponseOk()
         ->see('All posts');
}

If you are on an older Testbench without defineRoutes, register routes in setUp() after parent::setUp() via $this->app['router']->get(...) — same effect.

Implementation Patterns

Layered base test case. Package tests rarely need every hook; put routing + provider + config in an abstract TestCase, then create thin subclasses per feature (BrowserTestCase, ApiTestCase) that add traits like RefreshDatabase. Because BrowserKit state (session, cookies, crawler) persists per test method, subclasses stay clean.

Chained page flows. The value over plain $this->get() is stateful interaction: one test walks the whole journey.

public function test_new_user_registration()
{
    $this->visit('/register')
         ->type('Taylor', 'name')
         ->type('[email protected]', 'email')
         ->check('terms')
         ->select('GB', 'country')
         ->press('Register')
         ->seePageIs('/dashboard')
         ->seeRouteIs('dashboard')
         ->see('Hello, Taylor');
}

Auth via actingAs, not manual session. For package tests, define a minimal guard in getEnvironmentSetUp (or reuse your package's model) and then:

$this->actingAs($user)              // default guard
     ->actingAs($user, 'api')       // named guard
     ->withSession(['foo' => 'bar'])
     ->visit('/admin')
     ->see('Admin');

withSession() before the first visit() is the cheap way to pre-seed flashed data or CSRF tokens.

JSON endpoints — stay in the same test case. json(), get(), post() etc. chain into seeJson, seeJsonEquals, seeJsonStructure. Use seeJsonStructure with * for collection shapes; it is tolerant of extra keys, which keeps package tests from breaking when you add a field.

$this->json('POST', '/api/users', ['name' => 'Sally'])
     ->seeJsonStructure(['data' => ['*' => ['id', 'name', 'email']]]);

Mix chain API with raw assertions. When you need the actual response object (headers, status, view data), call() returns an Illuminate\Http\Response while still populating the crawler for later see() calls:

$response = $this->call('POST', '/posts', ['title' => 'Hi']);
$this->assertEquals(302, $response->status());
$this->assertRedirectedToRoute('posts.show', ['post' => 1]);
$this->assertSessionHas('status');

Compose with PHPUnit assertions and view inspection. assertViewHas, assertViewHasAll, assertViewMissing, assertHasOldInput, assertSessionHasErrors are the ones package authors actually use — they let you validate that your package's view composers and validation wiring behave, without asserting HTML strings.

Reusable intent helpers. Because chain methods return $this, wrap repeated journeys:

protected function loginAsAdmin(): static
{
    return $this->actingAs($this->admin)->visit('/admin');
}

Gotchas and Tips

  • The version number tracks Laravel, not Testbench. orchestra/testbench-browser-kit:^9.0 is for Laravel 11. Composer will let you install a mismatch; the failure shows up as kernel/container errors at runtime, not at install time. Pin it alongside orchestra/testbench.
  • $baseUrl is not optional. Declare public $baseUrl = 'http://localhost'; in the base test case, no trailing slash, or visit() throws about a missing host.
  • CSRF will bite you on press(). Package routes registered under the web middleware group run through VerifyCsrfToken; a simulated POST has no token. Fix with $this->withoutMiddleware() per test, the WithoutMiddleware trait on the class, or by seeding withSession(['_token' => 'test'])->type('test', '_token').
  • see() matches raw HTML, not text. &amp;, entity-escaped output and whitespace inside tags all break substring matches. When in doubt assert on seePageIs()/assertViewHas() or dd($this->response->getContent()) to see what the crawler actually received.
  • press() locates the button by visible text or name. Two buttons labelled "Save" make the assertion ambiguous — pass the name attribute instead.
  • attach() takes a filesystem path, not UploadedFile::fake(). Create real temp files in tests/fixtures, and pair with Storage::fake() so your package's upload handler has a fake disk. Clean up in tearDown() if you generate files at runtime.
  • Exceptions surface instead of becoming 500s. That is usually a debugging win (withoutExceptionHandling is unnecessary here), but it means an expected 500-status test must not rely on the framework's exception handler formatting.
  • State is sticky within a test. Session, cookies and the crawler carry across chained calls — good for journeys, confusing when a "fresh" request in the same test still sees the logged-in user. Split into separate test methods when you need isolation.
  • Named routes need to be registered before use. visitRoute()/seeRouteIs() fail with RouteNotFoundException if you registered routes via a raw path string or after setUp().
  • Debug with $this->response and $this->crawler. dump($this->response->getStatusCode(), $this->response->getContent()) beats guessing which see() failed; $this->crawler->filter('form')->html() shows what the form filler sees.
  • Long-term direction: Laravel's own BrowserKit testing was superseded by core HTTP tests ($this->get('/')->assertSee('Laravel')). Orchestra keeps shipping aligned releases, but treat this as a compatibility layer — write new package tests with the modern get()/assertSee/assertViewHas API, and keep BrowserKit for genuinely interactive flows (multi-step forms, link-crawl navigation) or for porting existing suites. Its practical extension points are just overriding methods on your base TestCase; there is no config file to publish.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator