symfony/browser-kit
Symfony BrowserKit simulates a web browser in PHP for testing and automation: make requests, follow links, click buttons, and submit forms programmatically. Includes an implementation powered by Symfony HttpClient for real HTTP requests.
Install the Package:
composer require symfony/browser-kit
(No publisher needed; it’s a standalone component.)
First Use Case: Basic Request Simulation
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
// Create a browser instance
$client = HttpClient::create();
$browser = new HttpBrowser($client);
// Simulate a GET request
$response = $browser->request('GET', 'https://example.com');
$content = $response->getContent();
// Extract data (e.g., title)
$crawler = $browser->getCrawler();
$title = $crawler->filter('title')->text();
Key Starting Points:
HttpClient (Symfony’s client, bundled with Laravel) for consistency.PHPUnit for assertions (e.g., assertStringContainsString).use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\DomCrawler\Crawler;
public function testLoginFlow()
{
$client = HttpClient::create();
$browser = new HttpBrowser($client);
// 1. Navigate to login page
$browser->request('GET', route('login'));
$crawler = $browser->getCrawler();
// 2. Fill and submit the form
$form = $crawler->selectButton('Login')->form([
'email' => 'user@example.com',
'password' => 'securepassword',
]);
$browser->submit($form);
// 3. Assert success (e.g., redirect or welcome message)
$this->assertStringContainsString('Welcome', $browser->getCrawler()->html());
}
// Handle multi-step forms (e.g., checkout)
$browser->request('GET', route('checkout.step1'));
// Fill and submit step 1
$form = $browser->getCrawler()->selectButton('Continue')->form([
'shipping_address' => '123 Main St',
]);
$browser->submit($form);
// Proceed to step 2
$browser->request('GET', $browser->getHistory()->current()->getUri());
$form = $browser->getCrawler()->selectButton('Place Order')->form([
'payment_method' => 'credit_card',
]);
$browser->submit($form);
use Symfony\Component\BrowserKit\AbstractBrowser;
use Illuminate\Support\Facades\Http;
public function scrapeProductData()
{
$client = HttpClient::create();
$browser = new AbstractBrowser($client);
$browser->request('GET', 'https://store.example.com/products');
$crawler = $browser->getCrawler();
$products = [];
foreach ($crawler->filter('.product-card') as $card) {
$products[] = [
'name' => $crawler->filter('.product-name', $card)->text(),
'price' => $crawler->filter('.price', $card)->text(),
];
}
// Store in DB or queue for processing
Product::insert($products);
}
// In a Laravel Artisan command or CI script
public function runPreDeployTests()
{
$browser = new HttpBrowser(HttpClient::create());
// Test critical paths
$this->testLoginFlow($browser);
$this->testCheckoutFlow($browser);
$this->testPaymentProcessing($browser);
if ($browser->getHistory()->hasError()) {
throw new \RuntimeException('Pre-deploy validation failed!');
}
}
HttpClient::create() for consistency with Laravel’s ecosystem.DomCrawler for CSS selectors and XPath queries.$browser->getCookieJar()->set('user_token', 'abc123');
$form = $crawler->selectButton('Upload')->form([
'file' => new \Symfony\Component\HttpFoundation\File\UploadedFile(
__DIR__.'/test.pdf',
'test.pdf',
'application/pdf'
),
]);
$browser->submit($form);
// In a PHPUnit test case
use Symfony\Component\BrowserKit\HttpBrowser;
use Tests\TestCase;
class LoginTest extends TestCase
{
public function testLogin()
{
$browser = new HttpBrowser(HttpClient::create());
$browser->request('GET', route('login'));
$this->assertEquals(200, $browser->getResponse()->getStatusCode());
$this->assertSelectorTextContains('h1', 'Login');
}
}
No JavaScript Execution:
Cookie Handling Quirks:
DateTime objects) in newer versions (v7.4.3+).time() or strtotime() for timestamps:
$cookie = new \Symfony\Component\HttpFoundation\Cookie(
'user_token',
'abc123',
time() + 3600 // Integer expiration
);
$browser->getCookieJar()->set($cookie);
History Tracking:
History object may misinterpret URLs with query parameters without slashes (e.g., ?id=123 vs. /path?id=123).$this->assertStringContainsString('/checkout', $browser->getHistory()->current()->getUri());
File Uploads:
<input type="file">) may fail silently. Ensure files are provided even if optional:
$form['file'] = new \Symfony\Component\HttpFoundation\File\UploadedFile(
__DIR__.'/empty.txt',
'empty.txt',
'text/plain',
0, // Size
null,
true // Overwrite
);
Response Wrapping:
$response = $browser->getResponse();
$wrapped = $response->getContent();
$this->assertStringContainsString('Expected text', $wrapped);
Inspect Responses:
$response = $browser->getResponse();
dump($response->getStatusCode(), $response->getHeaders(), $response->getContent());
Enable Verbose Logging:
$client = HttpClient::create([
'headers' => ['User-Agent' => 'Laravel BrowserKit Test'],
'debug' => true, // Enable debug mode
]);
Check History:
foreach ($browser->getHistory() as $i => $request) {
echo "Request {$i}: {$request->getMethod()} {$request->getUri()}\n";
echo "Response: {$request->getResponse()->getStatusCode()}\n";
}
Use DomCrawler for Selectors:
$crawler = $browser->getCrawler();
$this->assertCount(1, $crawler->filter('#login-button'));
Custom Browser Middleware:
use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\HttpClient\HttpClientInterface;
class CustomBrowser extends AbstractBrowser
{
public function __construct(HttpClientInterface $client)
{
parent::__construct($client);
$this->getCookieJar()->set('custom_token', 'xyz789');
}
}
Override Request Handling:
AbstractBrowser to modify requests (e.g., add headers):
protected function createRequest($method,
How can I help you explore Laravel packages today?