mcustiel/phiremock-client
PHP client for Phiremock Server with a fluent API to define HTTP expectations, manage scenarios, and reset server state. Works with Guzzle 6 by default, or any PSR-18 HTTP client by overriding the factory for custom connections.
require-dev):
composer require-dev mcustiel/phiremock-client guzzlehttp/guzzle
tests/TestCase.php):
use Mcustiel\Phiremock\Client\Factory;
use Mcustiel\Phiremock\Client\Connection\{Host, Port};
protected function createPhiremockClient(): PhiremockClient
{
return Factory::createDefault()
->createPhiremockClient(new Host('localhost'), new Port('8080'));
}
// In a test method
$client = $this->createPhiremockClient();
$client->createExpectation(
on(getRequest('/api/users'))
->then(respond(200)->andBody('{"id": 1, "name": "Test User"}'))
);
Use the fluent interface for complex expectations:
$client->createExpectation(
on(
getRequest('/api/orders/{id}')
->andMethod(isEqualTo('POST'))
->andHeader('Authorization', contains('Bearer'))
->andBody(contains('{"status": "pending"}'))
)->then(
respond(201)
->andHeader('Location', contains('/api/orders/'))
->andBody('{"id": 2, "status": "created"}')
)
);
Leverage scenarios for multi-step workflows:
// Setup
$client->createExpectation(
on(getRequest('/api/login'))
->then(respond(200)->andBody('{"token": "abc123"}'))
->setScenario('auth')
);
// Transition state
$client->setScenarioState('auth', 'loggedIn');
// Verify subsequent requests
$client->createExpectation(
on(getRequest('/api/profile'))
->then(respond(200)->andBody('{"user": "admin"}'))
->setScenario('auth')
->setScenarioState('profileLoaded')
);
Reset state between tests:
public function tearDown(): void
{
$this->phiremockClient->reset(); // Clears expectations, requests, and scenarios
}
Use JSON Path for flexible response validation:
$client->createExpectation(
on(getRequest('/api/search'))
->then(
respond(200)
->andBody(json_encode([
'results' => [
['name' => 'Item 1'],
['name' => 'Item 2']
]
]))
)
);
// Later, assert JSON Path in tests:
$response = Http::get('/api/search');
$this->assertEquals('Item 1', $response->jsonPath('$.results[0].name'));
Combine with Laravel’s HTTP testing:
public function test_user_creation()
{
$this->phiremockClient->createExpectation(
on(postRequest('/api/users'))
->then(respond(201)->andBody('{"id": 1}'))
);
$response = $this->postJson('/api/users', ['name' => 'John']);
$response->assertStatus(201);
}
Override the factory for non-Guzzle clients (e.g., Symfony HTTP Client):
use Symfony\Contracts\HttpClient\HttpClientInterface;
class SymfonyFactory extends Factory
{
public function createRemoteConnection(): HttpClientInterface
{
return \Symfony\Contracts\HttpClient\HttpClient::create();
}
}
// Usage:
$client = (new SymfonyFactory())
->createPhiremockClient(new Host('localhost'), new Port('8080'));
PHP Version Mismatch:
RuntimeException.^1.2 if stuck on PHP 8.1 or lower.composer.json for platform-check or use php -v in CI.Guzzle Version Conflicts:
ClassNotFoundException if not configured."guzzlehttp/guzzle": "6.5"
Scenario State Leaks:
$client->resetScenarios() in tearDown() or use reset() for full cleanup.JSON Path Quirks:
json_decode() first:
$data = json_decode($response->body(), true);
$this->assertEquals('value', $data['key']);
Port/Host Binding:
$this->artisan('phiremock:serve', ['--port' => 8080]);
List All Expectations:
$expectations = $this->phiremockClient->listExpectations();
dd($expectations); // Inspect in tests
Inspect Executed Requests:
$executions = $this->phiremockClient->listExecutions();
dd($executions); // Debug mismatched requests
Enable Phiremock Server Logging:
Add to phiremock-server config:
logging:
level: DEBUG
Validate JSON Path Queries: Use a tool like JSONPath Online Evaluator to test queries before coding.
Custom Matchers:
Extend Mcustiel\Phiremock\Client\Utils\Is to add domain-specific validators:
class CustomIs extends Is
{
public static function isValidToken(): Is
{
return new self(function ($value) {
return preg_match('/^Bearer [a-z0-9]{32}$/', $value);
});
}
}
Scenario State Transitions: Create a service to manage complex scenario logic:
class AuthScenario
{
public function __construct(private PhiremockClient $client)
{
}
public function login(string $token): void
{
$this->client->setScenarioState('auth', 'loggedIn');
// Additional logic...
}
}
Dynamic Expectation Generation:
Use Laravel’s collect() to generate expectations from test data:
$testCases = collect([
['url' => '/api/users', 'status' => 200, 'body' => '{}'],
['url' => '/api/posts', 'status' => 200, 'body' => '[]'],
]);
$testCases->each(function ($case) {
$this->phiremockClient->createExpectation(
on(getRequest($case['url']))
->then(respond($case['status'])->andBody($case['body']))
);
});
$this->phiremockClient->reset(); // Faster than clearing individually
Service Provider Binding:
Bind the client in AppServiceProvider for global access:
public function register()
{
$this->app->singleton(PhiremockClient::class, function ($app) {
return Factory::createDefault()
->createPhiremockClient(
new Host(config('phiremock.host')),
new Port(config('phiremock.port'))
);
});
}
Config Integration:
Add to config/phiremock.php:
return [
'host' => env('PHIREMOCK_HOST', 'localhost'),
'port' => env('PHIREMOCK_PORT', 8080),
'scheme' => env('PHIREMOCK_SCHEME', 'http'),
];
Test Helpers: Create a trait for reusable test logic:
trait PhiremockTests
How can I help you explore Laravel packages today?