symfony/browser-kit
Symfony BrowserKit simulates a web browser in PHP for functional tests: make requests, follow links, fill and submit forms, and inspect responses. Includes an HttpClient-based implementation to perform real HTTP requests programmatically.
Install via Composer:
composer require symfony/browser-kit
First Use Case: Testing a Form Submission
use Symfony\Component\BrowserKit\HttpFoundation\Session;
use Symfony\Component\BrowserKit\History;
use Symfony\Component\BrowserKit\Client;
use Symfony\Component\HttpClient\CurlHttpClient;
// In a PHPUnit test
public function testLoginForm()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/login'); // Simulate visiting the login page
$crawler = $client->getCrawler();
$form = $crawler->selectButton('Login')->form([
'email' => 'user@example.com',
'password' => 'secret',
]);
$client->submit($form); // Submit the form
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertSelectorTextContains('h1', 'Dashboard');
}
Key Starting Points:
Client: Simulates a browser (uses HttpClient under the hood).History: Tracks navigation (e.g., ->getCurrentRequest()).DomCrawler: Parses HTML responses (integrates with symfony/css-selector).HttpClient (Laravel’s Http facade) as the underlying client for consistency.Testing\TestCase for database transactions and auth.Pattern: Chain requests like a user journey.
public function testCheckoutFlow()
{
$client = new Client(new CurlHttpClient());
// Step 1: Add to cart
$client->request('POST', '/cart/add', [
'product_id' => 123,
'_token' => csrf_token(),
]);
// Step 2: Navigate to cart
$client->clickLink('View Cart');
// Step 3: Proceed to checkout
$form = $client->getCrawler()->selectButton('Checkout')->form();
$client->submit($form);
// Assert final state
$this->assertSame('/order/confirmation', $client->getHistory()->current()->getUri());
}
Pattern: Extend Laravel’s TestCase to reuse BrowserKit.
use Illuminate\Foundation\Testing\TestCase;
use Symfony\Component\BrowserKit\Client;
class BrowserKitTestCase extends TestCase
{
protected function createBrowserClient(): Client
{
return new Client($this->app->make('http.client'));
}
}
public function testWithLaravelIntegration()
{
$client = $this->createBrowserClient();
$client->request('GET', '/dashboard');
$this->assertTrue($client->getHistory()->isFirstPage());
$this->assertSelectorTextContains('h1', 'Welcome');
}
Pattern: Submit forms with files (e.g., uploads).
public function testFileUpload()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/upload');
$form = $client->getCrawler()->selectButton('Upload')->form();
$form['file'] = new \Symfony\Component\HttpFoundation\File\UploadedFile(
__DIR__.'/test.txt',
'test.txt',
'text/plain',
null,
true
);
$client->submit($form);
$this->assertStringContainsString('File uploaded', $client->getResponse()->getContent());
}
Pattern: Validate navigation paths.
public function testRedirectAfterLogin()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/login');
$client->submitForm('Login', [
'email' => 'user@example.com',
'password' => 'password',
]);
$history = $client->getHistory();
$this->assertSame('/dashboard', $history->current()->getUri());
$this->assertEquals(3, $history->count()); // Initial + login + redirect
}
Pattern: Simulate logged-in users.
public function testCookiePersistence()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/login');
$client->submitForm('Login', ['email' => 'user@example.com', 'password' => 'password']);
// Visit another page (cookies persist)
$client->request('GET', '/profile');
$this->assertTrue($client->getCookie('XSRF-TOKEN')->isSecure());
}
Pattern: Extract and validate parts of a page.
public function testFragmentValidation()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/dashboard');
$crawler = $client->getCrawler();
$fragment = $crawler->filter('.user-stats')->html();
$this->assertStringContainsString('Orders: 5', $fragment);
}
Pattern: Use Symfony’s PHPUnit helpers.
use Symfony\Component\BrowserKit\Tests\Constraint\BrowserHistoryIsOnLastPage;
public function testPagination()
{
$client = new Client(new CurlHttpClient());
$client->request('GET', '/posts');
$this->assertThat(
$client->getHistory(),
new BrowserHistoryIsOnLastPage()
);
}
CSRF Tokens:
$token = $client->getCrawler()->filter('input[name="_token"]')->attr('value');
$form['_token'] = $token;
File Uploads:
new \Symfony\Component\HttpFoundation\File\UploadedFile() with null for empty fields (fixed in v7.2.4+).Relative URLs:
/profile may break if the base URL isn’t set.$client->setServerParameter('HTTP_HOST', 'example.test');
Cookie Expiration:
time() + 3600) may fail.DateTime or DateTimeImmutable (supported in v7.4.3+).History Tracking:
->followRedirects(true) or manually chain requests.JavaScript-Rendered Content:
PHP 8.4+ Features:
DomCrawler::setHtml($html, $baseUri, 'html5').Inspect Responses:
$response = $client->getResponse();
$this->assertSame(200, $response->getStatusCode());
$this->assertStringContainsString('Expected text', $response->getContent());
Dump Crawler:
$crawler = $client->getCrawler();
dump($crawler->html()); // View raw HTML
Enable Verbose Logging:
$client = new Client(new CurlHttpClient(), [], [
'debug' => true,
]);
Check Headers:
$headers = $client->getResponse()->headers->all();
$this->assertArrayHasKey('Set-Cookie', $headers);
HttpClient Configuration:
Http client for consistency:
$client = new Client($this->app->make('http.client'));
Base URI:
$client->setServerParameter('HTTP_HOST', 'app.test');
$client->setServerParameter('REQUEST_URI', '/');
Server Parameters:
$client->setServerParameter('SERVER_NAME', 'localhost');
How can I help you explore Laravel packages today?