If you’re building a multi-tenant Laravel application that identifies tenants via subdomains (like tenant1.app.test, tenant2.app.test), you might run into issues when trying to set up Pest’s browser testing with Playwright. Here’s how to solve it.
The Problem
Pest 4 introduced powerful browser testing capabilities using Playwright. For multi-tenant apps, you’d naturally reach for the withHost() method to configure the subdomain:
// tests/Pest.php
pest()->browser()->withHost('mytenant.app.test')->inFirefox();
But when you run your tests, you get an error like:
Connection to 'mytenant.app.test:52197' failed
Why withHost() Doesn’t Work
The issue lies in how Playwright’s server binding works. When you use withHost(), it attempts to bind the Playwright server to that hostname. However, servers can only bind to IP addresses, not hostnames.
Looking at the Pest browser plugin’s ServerManager.php:
// This runs: playwright run-server --host mytenant.app.test
// Which fails because servers can't bind to hostnames
The HTTP server correctly binds to 127.0.0.1, but the Playwright server fails to start.
The Solution: Use serverVariables() Instead
The Pest browser plugin provides another mechanism for setting the Host header: the serverVariables() function. This bypasses the binding issue entirely by injecting the Host header into the request after the servers are running.
Step 1: Update tests/Pest.php
Remove withHost() and add a global serverVariables() function:
<?php
// tests/Pest.php
use Spatie\Multitenancy\Concerns\UsesMultitenancyConfig;
pest()->extend(Tests\TestCase::class)
->use(UsesMultitenancyConfig::class)
->in('Feature');
pest()->extend(Tests\TestCase::class)
->use(UsesMultitenancyConfig::class)
->in('Browser');
// Configure browser testing - no withHost()!
pest()->browser();
/**
* Server variables for browser tests to support multi-tenant subdomain architecture.
* This sets the Host header for Laravel's subdomain-based tenant detection.
*/
function serverVariables(): array
{
return [
'HTTP_HOST' => 'mytenant.app.test',
'SERVER_NAME' => 'mytenant.app.test',
];
}
Step 2: Add beforeEach in Browser Tests (Optional but Recommended)
For additional clarity and flexibility, you can also set server variables per test file:
<?php
// tests/Browser/ExampleBrowserTest.php
uses()->beforeEach(function () {
// Set up tenant context for browser tests via server variables
$this->serverVariables = [
'HTTP_HOST' => 'mytenant.app.test',
'SERVER_NAME' => 'mytenant.app.test',
];
});
it('can visit the login page', function () {
$page = visit('/login');
$page->assertSee('Login')
->assertNoJavascriptErrors();
});
Step 3: Ensure Your Hosts File Is Configured
Make sure your tenant subdomain resolves to localhost:
# /etc/hosts
127.0.0.1 mytenant.app.test
How It Works
The Pest browser plugin’s LaravelHttpServer.php calls test()->serverVariables() to get server variables that are merged into each request. By setting HTTP_HOST and SERVER_NAME, Laravel’s subdomain-based tenant detection works correctly.
// From LaravelHttpServer.php
$serverVariables = test()->serverVariables(); // Gets from your test
// These are merged into $_SERVER for the request
Testing Different Tenants
You can easily test different tenants by parameterizing your tests:
<?php
dataset('tenants', [
'tenant1' => ['tenant1.app.test'],
'tenant2' => ['tenant2.app.test'],
]);
it('shows the correct tenant dashboard', function (string $host) {
$this->serverVariables = [
'HTTP_HOST' => $host,
'SERVER_NAME' => $host,
];
$page = visit('/dashboard');
$page->assertSee('Dashboard')
->assertNoJavascriptErrors();
})->with('tenants');
Running the Tests
# Run all browser tests
./vendor/bin/pest tests/Browser
# Run with debug mode (opens browser visibly)
./vendor/bin/pest tests/Browser --debug
# Run a specific test
./vendor/bin/pest tests/Browser/ExampleBrowserTest.php
Summary
When using Pest browser testing with a multi-tenant subdomain architecture:
- Don’t use
withHost()– it breaks Playwright server binding - Do use the
serverVariables()function to setHTTP_HOSTandSERVER_NAME - Configure your
/etc/hostsfile to resolve tenant subdomains to127.0.0.1
This approach lets you enjoy the full power of Pest’s browser testing while maintaining proper tenant context in your multi-tenant Laravel application.