donatj/mock-webserver
Lightweight PHP mock web server for tests and local development. Spin up an HTTP server on a random port, enqueue canned responses, inspect received requests, and simulate endpoints reliably. Handy for integration tests without external services.
Installation Update Composer to ensure compatibility with PHP 7.2+ (now mandatory due to security updates):
composer require --dev donatj/mock-webserver:^2.10.0
No manual service provider registration is needed—the package is auto-discovered.
Basic Usage Start a mock server in a test (PHP 7.2+ required):
use Donatj\MockWebserver\MockWebserver;
public function test_example()
{
$server = new MockWebserver();
$server->start(); // Uses PHP_BINARY internally (resolves PATH issues)
// Get the mock server URL
$response = Http::get($server->getUrl('/api/test'));
$this->assertEquals(200, $response->status());
}
Key Entry Points
MockWebserver class: Core class for managing routes, responses, and server state.MockWebserver::start(): Launches the server on a random port (now more reliable with PHP_BINARY fix).MockWebserver::getUrl(string $path): Generates a full URL for testing.MockWebserver::addRoute(): Registers mock routes (GET, POST, etc.).$server = new MockWebserver();
$server->start();
// Mock a GET endpoint
$server->addRoute('GET', '/api/users', fn() =>
response()->json(['id' => 1, 'name' => 'John Doe'])
);
$response = Http::get($server->getUrl('/api/users'));
$this->assertJson($response->json());
$server->addRoute('POST', '/api/users', function ($request) {
$data = json_decode($request->getBody(), true);
return response()->json(['success' => true, 'data' => $data]);
});
Http::post($server->getUrl('/api/users'), ['name' => 'Jane']);
$server->addRoute('POST', '/api/upload', function ($request) {
$file = $request->file('file');
return response()->json(['filename' => $file->getClientOriginalName()]);
});
Http::post($server->getUrl('/api/upload'), [
'file' => UploadedFile::fake()->create('test.txt', 100),
]);
public function test_login_flow()
{
$server = new MockWebserver();
$server->start();
$server->addRoute('POST', '/api/login', fn() =>
response()->json(['token' => 'mock-token'])
);
$response = Http::post($server->getUrl('/api/login'), ['email' => 'test@example.com']);
$this->assertEquals('mock-token', $response->json('token'));
}
Use regex or closures for flexible routing:
$server->addRoute('GET', '/api/users/{id}', function ($request) {
$id = $request->route('id');
return response()->json(['id' => $id]);
});
Use a static instance or dependency injection:
// In a test trait
protected $mockServer;
protected function setUp(): void
{
$this->mockServer = new MockWebserver();
$this->mockServer->start();
}
protected function tearDown(): void
{
$this->mockServer->stop(); // Critical: Frees ports
}
Replace third-party APIs (e.g., Stripe, PayPal) with local mocks:
$server->addRoute('POST', '/webhooks/stripe', function () {
return response()->json(['type' => 'payment_intent.succeeded']);
});
Simulate middleware behavior in mock responses:
$server->addRoute('GET', '/api/protected', function () {
if (!auth()->check()) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return response()->json(['data' => 'secret']);
});
PHP 7.2+ Mandatory
composer require php:^7.2 --dev
v2.9.0 if stuck on older PHP (not recommended).PHP_BINARY Fix for PATH Issues
PATH (e.g., macOS 14.3+).PHP_BINARY internally. If issues persist:
putenv('PHP_BINARY=' . escapeshellarg('/usr/local/bin/php'));
Port Conflicts
$server->setPort(8080);
Enable Verbose Logging
$server->setVerbose(true); // Logs server startup/shutdown
Inspect Requests Add a debug route:
$server->addRoute('*', '/debug', function ($request) {
\Log::debug('Request:', [
'method' => $request->method(),
'path' => $request->path(),
'headers' => $request->headers(),
'body' => $request->getBody(),
]);
});
Check Port Usage If tests hang, verify no stale processes:
lsof -i :<port>
CORS Headers The mock server does not enforce CORS by default. Add headers manually:
$server->addRoute('GET', '/api/data', function () {
return response()
->json(['data' => 'test'])
->header('Access-Control-Allow-Origin', '*');
});
Custom Response Factories
Extend MockWebserver for reusable helpers:
class CustomMockWebserver extends MockWebserver
{
public function jsonResponse($data, $status = 200)
{
$this->addRoute('GET', '/api/json', fn() =>
response()->json($data, $status)
);
}
}
Middleware Simulation Override route handling:
$server->addRoute('GET', '/api/middleware', function ($request) {
if ($request->header('X-API-KEY') !== 'secret') {
return response()->json(['error' => 'Unauthorized'], 403);
}
// ...
});
How can I help you explore Laravel packages today?