jcchavezs/httptest
Spin up a lightweight local HTTP server for integration testing real HTTP calls (e.g., cURL). Define a request handler to assert method/headers/body and craft responses, enabling client- and server-side assertions without mocking HTTP.
Installation Add the package via Composer:
composer require --dev jcchavezs/httptest
Register the service provider in config/app.php (if not auto-discovered):
'providers' => [
// ...
Jcchavezs\Httptest\HttptestServiceProvider::class,
],
First Test Case
Use the HttpTest trait in your test class:
use Jcchavezs\Httptest\Httptest;
class ExampleTest extends TestCase
{
use Httptest;
public function test_get_request()
{
$this->get('/api/users')
->seeStatusCode(200)
->seeJson(['id' => 1]);
}
}
Key Entry Points
seeStatusCode(), seeJson(), seeHeader().Stub a third-party API (e.g., Stripe, GitHub) to avoid real calls:
$this->stub('GET', 'https://api.stripe.com/v1/charges', [
'status' => 200,
'body' => json_encode(['id' => 'ch_123']),
]);
Override Laravel’s routes for isolated testing:
$this->overrideRoutes(function ($router) {
$router->get('/test', function () {
return response()->json(['message' => 'Test']);
});
});
Fluent interface for readability:
$this->post('/login')
->seeStatusCode(422)
->seeJsonValidationErrors(['email']);
Simulate middleware responses without hitting the full stack:
$this->withMiddleware(\App\Http\Middleware\Authenticate::class)
->get('/admin')
->seeStatusCode(302);
Combine with Laravel’s database testing:
public function test_create_user()
{
$this->post('/users', ['name' => 'John'])
->seeStatusCode(201)
->seeInDatabase('users', ['name' => 'John']);
}
Laravel Mix/Testing:
Use alongside laravel/http-tests or Pest for broader testing suites.
API Contract Testing: Validate responses against OpenAPI/Swagger specs by extending assertions.
Performance Testing: Stub slow external calls to focus on local logic (e.g., rate-limiting tests).
Legacy Code: Ideal for testing legacy apps where refactoring is costly—mock HTTP layers first.
Route Overrides Leak:
Forgetting to reset overrides between tests can cause flaky tests.
Fix: Use refreshRoutes() or withoutOverridingRoutes() in setUp().
Global State Pollution:
Stubbing globally (e.g., HttpTest::stub()) affects all tests.
Fix: Scope stubs to specific test methods or use HttpTest::reset().
Laravel 5.5+ Quirks: The package predates Laravel’s first-party HTTP testing. May conflict with:
Http::fake() (use HttpTest::stub() instead).Route::flushCachedRoutes()).JSON Assertions:
seeJson() uses loose comparison. For strict checks, combine with assertEquals():
$response = $this->get('/data')->json();
$this->assertEquals(['key' => 'value'], $response);
Inspect Stubbed Requests:
Use HttpTest::getStubbedRequests() to verify what’s been mocked.
Enable Verbose Logging:
Set config('httptest.verbose', true) to log all stubbed interactions.
Test External API Calls:
Temporarily disable stubs with HttpTest::disableStubbing() to confirm real calls work.
Custom Assertions: Extend the trait to add domain-specific checks:
use Jcchavezs\Httptest\Httptest;
trait MyCustomAssertions
{
public function seePagination(array $expected)
{
$response = $this->response;
$this->assertArrayHasKey('data', $response->json());
$this->assertArrayHasKey('meta', $response->json());
// ...
}
}
Response Factories: Create reusable response templates:
$this->stub('GET', '/api/users', (new UserResponseFactory())->create());
Middleware Hooks: Override middleware logic for testing edge cases:
$this->withMiddleware(function ($request, $next) {
if ($request->path() === '/admin') {
abort(403);
}
return $next($request);
});
Legacy Laravel Support: For Laravel <5.5, alias the trait to avoid conflicts:
class TestCase extends \Illuminate\Foundation\Testing\TestCase
{
use \Jcchavezs\Httptest\Httptest as HttpTest;
}
How can I help you explore Laravel packages today?