Installation
composer require --dev draw/http-tester
Basic Test Setup
Use the Tester trait in your PHPUnit test class:
use Draw\HttpTester\Tester;
class ApiTest extends TestCase
{
use Tester;
}
First Test Case Test a simple GET request with assertions:
public function test_api_returns_expected_data()
{
$this->get('/api/users')
->assertStatus(200)
->assertJsonStructure(['data' => ['*' => ['id', 'name']]]);
}
Key Methods to Explore First
$this->get(), $this->post(), etc. for HTTP methods.assertStatus(), assertBodyContains(), assertJson() for assertions.withHeaders(), withQueryParams() for request modifications.Validate responses against expected schemas:
public function test_user_endpoint_contract()
{
$this->get('/api/users/1')
->assertStatus(200)
->assertJsonStructure([
'id', 'name', 'email',
'created_at', 'updated_at'
]);
}
Simulate external API responses without network calls:
public function test_payment_gateway_integration()
{
$this->post('/api/payments', ['amount' => 100])
->assertStatus(200)
->assertJsonPath('$.transaction_id', 'txn_12345');
}
Test error scenarios deterministically:
public function test_authentication_failure()
{
$this->withHeaders(['Authorization' => 'invalid'])
->get('/api/protected')
->assertStatus(401)
->assertJson(['error' => 'Unauthorized']);
}
Test route middleware behavior:
public function test_admin_middleware()
{
$this->withHeaders(['X-Admin' => 'false'])
->get('/admin/dashboard')
->assertStatus(403);
}
Use alongside Laravel's built-in testing tools:
public function test_laravel_integration()
{
$response = $this->get('/api/users');
$this->assertDatabaseCount('users', 3);
$this->assertStatus($response, 200);
}
Register HttpTester as a service for dependency injection:
// In a test service provider
$this->app->singleton(HttpTester::class, function () {
return new HttpTester();
});
Create reusable assertion methods:
protected function assertApiSuccess($response)
{
$this->assertStatus($response, 200);
$this->assertJson($response);
$this->assertJsonStructure($response, ['data', 'meta']);
}
Validate API resource transformations:
public function test_user_resource()
{
$this->get('/api/users/1')
->assertStatus(200)
->assertJsonPath('$.data.name', 'John Doe');
}
PSR-7 Compatibility Issues
getResponse() to inspect raw PSR-7 objects:
$response = $this->get('/')->getResponse();
$this->assertInstanceOf(Psr\Http\Message\ResponseInterface::class, $response);
Missing CSRF Token
$this->post('/form', [
'_token' => csrf_token(),
'data' => 'value'
]);
Session Handling
withSession():
$this->withSession(['user_id' => 1])
->get('/dashboard');
Guzzle Under the Hood
$this->get('/redirect-endpoint', ['allow_redirects' => false]);
Assertion Order Sensitivity
$response = $this->get('/');
$this->assertStatus($response, 200);
$this->assertBodyContains($response, 'Welcome');
Inspect Raw Response
$response = $this->get('/')->getResponse();
file_put_contents('debug_response.txt', $response->getBody());
Enable Verbose Output
$this->get('/')->setVerbose(true);
Check Headers and Cookies
$this->get('/')
->assertHeader('Content-Type', 'application/json')
->assertCookie('session', 'value');
Default Assertion Behavior
$this->assertStatus($response, 200, false);
Custom Matchers
$this->get('/')
->assertCustom(function ($response) {
$body = json_decode($response->getBody(), true);
$this->assertArrayHasKey('expected_key', $body);
});
Performance Considerations
$this->setVerbose(false);
Custom HTTP Client
$this->setClient(new GuzzleHttp\Client(['base_uri' => 'http://test.local']));
Middleware Hooks
$this->addMiddleware(function ($request) {
$request = $request->withHeader('X-Custom', 'Header');
return $request;
});
Response Transformers
$this->get('/')
->transformResponse(function ($response) {
return $response->withHeader('X-Processed', 'true');
})
->assertHeader('X-Processed', 'true');
Integration with Laravel Events
$this->on('beforeRequest', function ($request) {
// Modify request before sending
});
Custom Assertion Classes
class CustomAssertions extends Assertions
{
public function assertCustomValidation($response, $rules)
{
// Custom validation logic
}
}
How can I help you explore Laravel packages today?