Install via Composer (dev dependency):
composer require-dev mcustiel/phiremock guzzlehttp/guzzle
Start the Phiremock Server:
vendor/bin/phiremock-server
(Default port: 8080. Configure via --port flag if needed.)
First Mock in a Test:
use Mcustiel\Phiremock\Client;
use Mcustiel\Phiremock\Expectation;
// In your Laravel test (e.g., FeatureTest)
public function test_mocked_api_call()
{
$client = new Client('http://localhost:8080');
$client->addExpectation(
Expectation::create()
->withMethod('GET')
->withUrl('/api/users')
->withResponse(200, ['name' => 'John Doe'])
);
// Make a request to your Laravel app that calls the mocked endpoint
$response = Http::get('http://your-app.test/api/external-users');
$this->assertEquals('John Doe', $response['name']);
}
Verify Requests:
$requests = $client->getRequests();
$this->assertCount(1, $requests);
Mock Stripe’s API to test a payment flow without hitting live endpoints:
$client->addExpectation(
Expectation::create()
->withMethod('POST')
->withUrl('/v1/charges')
->withHeader('Authorization', 'Bearer sk_test_*')
->withBodyMatches('/"amount":1000/') // Regex match
->withResponse(200, [
'id' => 'ch_123',
'status' => 'succeeded'
])
);
Define Mocks Early:
Create JSON expectation files (e.g., tests/phiremock/stripe.json) for reusable mocks:
{
"expectations": [
{
"method": "POST",
"url": "/v1/charges",
"headers": { "Authorization": "Bearer sk_test_.*" },
"bodyPattern": "\"amount\":1000",
"response": {
"status": 200,
"body": { "id": "ch_123", "status": "succeeded" }
}
}
]
}
Load them in setUp():
$client->loadExpectationsFromDirectory(__DIR__.'/phiremock');
Dynamic Responses:
Use request data in responses (e.g., echo back a user_id):
$client->addExpectation(
Expectation::create()
->withMethod('POST')
->withUrl('/api/register')
->withResponse(201, [
'user_id' => '$request.body.user_id', // Dynamic value
'message' => 'User created'
])
);
Stateful Testing: Simulate sequences (e.g., OAuth token flow):
// First call: Return a token
$client->addExpectation(
Expectation::create()
->withMethod('POST')
->withUrl('/oauth/token')
->withResponse(200, ['access_token' => 'abc123'])
);
// Second call: Require the token
$client->addExpectation(
Expectation::create()
->withMethod('GET')
->withUrl('/api/protected')
->withHeader('Authorization', 'Bearer abc123')
->withResponse(200, ['data' => 'secret'])
);
Latency Simulation: Add artificial delay for realistic testing:
$client->addExpectation(
Expectation::create()
->withMethod('GET')
->withUrl('/api/slow-endpoint')
->withLatency(1500) // 1.5 seconds
->withResponse(200, ['data' => 'loaded'])
);
Service Provider:
Register Phiremock as a singleton in AppServiceProvider:
public function register()
{
$this->app->singleton('phiremock.client', function () {
return new Client('http://phiremock:8080');
});
}
Test Helpers: Create a base test case:
abstract class PhiremockTestCase extends TestCase
{
protected $client;
protected function setUp(): void
{
parent::setUp();
$this->client = app('phiremock.client');
$this->client->reset(); // Clear expectations
}
protected function mockApi($method, $url, $response)
{
$this->client->addExpectation(
Expectation::create()
->withMethod($method)
->withUrl($url)
->withResponse($response)
);
}
}
Proxy Mode: Forward requests to a real API during development:
$client->addExpectation(
Expectation::create()
->withMethod('GET')
->withUrl('/api/external')
->withProxy('https://real-api.example.com')
);
Extension Setup: Install the extension:
composer require-dev mcustiel/phiremock-codeception-extension
Configure in codeception.yml:
extensions:
enabled:
- Mcustiel\Phiremock\Codeception\Extension
Module Usage:
<?php
$I = new AcceptanceTester($scenario);
$I->wantTo('Mock a payment API');
$I->haveHttpMockExpectation(
'POST',
'/v1/charges',
200,
['id' => 'ch_123']
);
$I->sendGET('/checkout');
$I->seeResponseCodeIs(200);
Port Conflicts:
8080) may clash with other services. Use --port flag or configure in config/phiremock.php (if available).vendor/bin/phiremock-server --port $(shuf -i 8080-9999 -n 1)
Stateful Mocks Leak Between Tests:
$client->reset() in setUp() or tearDown().->withStateful(false) for stateless mocks or wrap in transactions:
DB::beginTransaction();
try {
// Test logic
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
}
Regex Matching Quirks:
withBodyMatches()) can be fragile. Prefer exact matches (withBody()) for simple cases.->withBodyMatches('/"amount":\\d+/') // Matches "amount": 100
Latency Simulation Inconsistencies:
withLatency()) is approximate and may vary across environments (e.g., Docker vs. local).usleep() in tests for deterministic delays:
usleep(1500000); // 1.5 seconds
JSON Response Parsing:
->withResponse(200, json_encode(['key' => 'value']))
Proxy Mode Limitations:
withProxy()) does not preserve the original request’s headers/body for verification.Inspect Requests: Use the REST API to debug:
$requests = $client->getRequests();
dd($requests[0]->getBody()); // Inspect raw request body
Enable Verbose Logging: Start the server with:
vendor/bin/phiremock-server --verbose
Reset Stuck State: If expectations behave unexpectedly, reset the server:
vendor/bin/phiremock-server --reset
Priority Conflicts: When multiple expectations match, the first one
How can I help you explore Laravel packages today?