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

Browser Kit Testing Laravel Package

laravel/browser-kit-testing

Laravel BrowserKit Testing gives your Laravel apps a fluent API for making HTTP requests, inspecting responses, and filling out forms. Extend the provided TestCase base class and write expressive visit, see, and dontSee tests with no extra setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a compatibility shim, not a testing framework. It restores the pre-5.4 BrowserKit testing API (visit, click, see, type, press, seeJson) on top of modern Laravel releases. Reach for it when you have an existing suite written against Laravel ≤5.3 and you've upgraded the app, but can't afford to rewrite several hundred tests in the same sprint.

Install and wire it up in three steps:

composer require laravel/browser-kit-testing --dev
<?php

namespace Tests;

use Laravel\BrowserKitTesting\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    public $baseUrl = 'http://localhost';
}

Then run one existing test before touching anything else:

public function testHomePage()
{
    $this->visit('/')->see('Laravel');
}

Before you commit to the upgrade path, verify the package actually supports your stack — the constraint matters more than the star count:

composer why-not laravel/browser-kit-testing

First real use case is almost always a form or auth flow: visit('/login')->type(...)->press('Login')->seePageIs('/dashboard'). That single chain tells you whether the shim is wired correctly, because it exercises session, CSRF, routing, and redirects in one go.

Implementation Patterns

Run two base classes side by side. The single highest-leverage pattern for a migration is not converting everything at once. Keep a Tests\BrowserKitTestCase (extending this package) and add a Tests\TestCase (extending Illuminate\Foundation\Testing\TestCase). New tests use the new base; legacy tests stay on the old one. Port opportunistically.

Exploit statefulness for journey tests. BrowserKit keeps cookies and session across sequential visit() calls within one test method. This is the one area where it's genuinely nicer than the modern API for multi-step flows:

public function testCheckoutWizard()
{
    $this->actingAs($user)
         ->visit('/cart')
         ->press('Checkout')
         ->type('123 Main St', 'address')
         ->press('Continue')
         ->select('express', 'shipping_method')
         ->press('Place Order')
         ->seePageIs('/orders/confirmed');
}

Authentication and session. actingAs($user), actingAs($user, 'api'), and withSession([...]) all chain and persist. Combine with model factories exactly as you would today.

Forms. type, select, check/uncheck, attach($realPath, 'photo'), press('Button text'). click('About Us') matches on link text, not selector — keep your link copy stable in tests or use visitRoute() to sidestep the DOM entirely.

JSON APIs. json('POST', '/user', [...])->seeJson([...]) for fragments, seeJsonEquals() for exact payloads, seeJsonStructure() (including nested * wildcards) for shape. Note the naming: these map 1:1 to assertJson, assertExactJson, assertJsonStructure.

Escape hatch to raw responses. When you need status codes or the actual body, $response = $this->call('GET', '/') returns an Illuminate\Http\Response and lets you drop back to vanilla PHPUnit assertions. Use this for the assertions BrowserKit doesn't cover.

BrowserKit (old) Modern equivalent
seeJson([...]) assertJson([...])
seeJsonEquals([...]) assertExactJson([...])
seePageIs($uri) assertLocation($uri) / assertRedirect
assertResponseOk() assertOk()
assertViewHas($k, $v) assertViewHas($k, $v) (unchanged)

When you port a test, port the whole class — mixing APIs inside one class is where things get weird (see below).

Gotchas and Tips

Never mix old and new request methods in one test class. Laravel\BrowserKitTesting\TestCase::get() returns the BrowserKit client ($this), while the modern get() returns a TestResponse. Chaining ->assertSee() after a BrowserKit request fails with a confusing method-not-found. This is the most common first-day stumble.

Exceptions are rendered, not thrown. BrowserKit requests go through the full HTTP kernel, so a 500 becomes a "Whoops" error page in the response. Your see() assertions then fail with a message about missing text rather than the real stack trace. When a test fails mysteriously, dump $this->response->getContent() or check $this->response->status() before assuming your assertion is wrong.

$baseUrl is mandatory. Omitting it produces an unhelpful runtime error deep in the request cycle. Set it once on the base class, and make sure APP_URL in phpunit.xml is http://localhost so generated URLs match what seePageIs() compares against.

CSRF only disables itself when the environment is testing. If you've hand-rolled a phpunit.xml or override the environment, form submissions will 419 and you'll spend an hour on it. Verify APP_ENV=testing is actually in effect.

attach() wants a real filesystem path, not an UploadedFile::fake() instance. Keep a small tests/fixtures/ directory with sample uploads; if you need in-memory fakes, that's a signal to port the test to the modern API.

PHPUnit version drift is the real risk. Old releases of this package leaned on assertArraySubset, which was removed in PHPUnit 8. The 2026 release exists largely to keep constraints aligned — pin your phpunit/phpunit version deliberately and run the full suite immediately after any composer update touching this package.

Extension points worth using: subclass the provided TestCase and add your own fluent helpers (domain-specific ->loginAsAdmin() chains), override createApplication() if your bootstrap differs from the stock CreatesApplication trait, and use withoutMiddleware() per-test rather than the class-level trait when a middleware-disabling behaviour doesn't appear to take effect — the global mechanism is less reliable under the newer kernel than the per-test call.

Treat it as debt with an expiry date. It exists to defer a migration, not to avoid it. Track how many test classes still extend the BrowserKit base and shrink the number each sprint — otherwise you're permanently locked to a shim whose only job is backwards compatibility.

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.
vento/laravel-airbrake
wfeller/laravel-batch
maco-studios/mage-os-installer
busarm/armie
siberfx/laravel-logger
kfteam1991/dcat-admin
dashed/dashed-laposta
dashed/dashed-ecommerce-montaportal
dashed/dashed-ecommerce-etsy
neohq/string-blade-compiler
log1x/acf-composer
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager