laravel/browser-kit-testing
Fluent BrowserKit-style testing for Laravel apps: make HTTP requests, navigate pages, assert response content, and interact with forms in functional tests. Install as a dev dependency and extend Laravel\BrowserKitTesting\TestCase to get started.
Installation:
composer require laravel/browser-kit-testing --dev
Update Base Test Case:
Replace Illuminate\Foundation\Testing\TestCase with Laravel\BrowserKitTesting\TestCase in your Tests/TestCase.php:
use Laravel\BrowserKitTesting\TestCase as BaseTestCase;
First Test:
public function testBasicPageLoad()
{
$this->visit('/')
->see('Welcome'); // Assert text exists
}
visit(), see(), dontSee(), click(), type(), press() (for forms).json(), seeJson(), seeJsonEquals(), seeJsonStructure().actingAs(), withSession().withoutMiddleware() trait/method.Test a simple route with form submission:
public function testUserRegistration()
{
$this->visit('/register')
->type('John Doe', 'name')
->press('Register')
->seePageIs('/dashboard');
}
public function testLoginFlow()
{
$this->visit('/login')
->type('user@example.com', 'email')
->type('password123', 'password')
->press('Login')
->seePageIs('/dashboard');
}
public function testCreateUserAPI()
{
$this->json('POST', '/api/users', ['name' => 'Jane'])
->seeJsonEquals(['success' => true]);
}
public function testProtectedRoute()
{
$user = User::factory()->create();
$this->actingAs($user)
->visit('/profile')
->see('Welcome, ' . $user->name);
}
public function testSessionPersistence()
{
$this->withSession(['theme' => 'dark'])
->visit('/')
->see('dark'); // Check if theme is applied
}
public function testControllerWithoutMiddleware()
{
$this->withoutMiddleware()
->visit('/admin')
->see('Unauthenticated Access'); // Bypass auth middleware
}
$user = User::factory()->create();
$this->actingAs($user)->visit('/profile');
refresh() for Dynamic Content:
$this->visit('/dashboard')->refresh()->see('Updated Data');
$this->visit('/login')->seePageIs('/dashboard'); // After login
$this->visit('/upload')
->attach(__DIR__.'/test.jpg', 'avatar')
->press('Upload')
->see('Upload Successful');
Middleware Leaks:
withoutMiddleware() can cause tests to fail if middleware (e.g., auth) isn’t handled.WithoutMiddleware trait or withoutMiddleware() method.Session State:
withSession() carefully.refresh() to reload the page.Dynamic Content:
see()/dontSee() may fail if content is dynamically loaded (e.g., AJAX).refresh() or wait for elements with sleep() (not ideal; prefer explicit waits).JSON Assertions:
seeJson() checks for partial matches, while seeJsonEquals() requires exact matches.seeJsonStructure() for flexible structural validation.Route Caching:
visitRoute().routes/web.php.$response = $this->call('GET', '/');
dd($response->getContent()); // Debug raw HTML
phpunit.xml:
<env name="BROWSERKIT_LOG" value="true"/>
$this->visit('/')->sleep(2)->see('Dynamic Content');
Custom Assertions:
Extend TestCase to add domain-specific assertions:
class CustomTestCase extends TestCase
{
public function seeErrorMessage($message)
{
return $this->see($message)->assertResponseStatus(422);
}
}
Hooks for Setup/Teardown:
Override setUp()/tearDown():
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware();
}
Mocking External Services:
Use Laravel’s HTTP clients or mocks with Mockery:
$this->mock(Http::class)->shouldReceive('get')->andReturn(...);
Custom Helpers:
Add methods to TestCase for reusable test logic:
public function loginAsAdmin()
{
$admin = User::factory()->admin()->create();
$this->actingAs($admin);
}
$baseUrl in TestCase if testing against a non-localhost environment:
protected $baseUrl = 'https://staging.example.com';
attach() are relative to the test’s working directory. Use absolute paths for reliability:
$this->attach(__DIR__.'/../../storage/test.jpg', 'file');
How can I help you explore Laravel packages today?