HTTP testing helpers — get(), post(), assertStatus()
Concept
HTTP testing helpers — get(), post(), assertStatus() — let tests make fake HTTP requests through the full application stack without a real web server. The response is captured and assertions are made on it.
How it works:
- Build a
Requestobject from the test parameters. - Pass it through the application's HTTP kernel (middleware + router + controller).
- Capture the
Responseobject. - Return a
TestResponsewrapper that provides assertion methods.
TestResponse wrapper: Wraps the framework's Response with assertion methods:
assertStatus(int $code): Assert the HTTP status code.assertOk(): Assert 200.assertJson(array $expected): Assert the response body contains this JSON (subset match).assertJsonFragment(array $data): Assert the JSON contains this fragment anywhere.assertSee(string $text): Assert the response body contains this text.assertRedirect(?string $uri = null): Assert the response is a 3xx redirect, optionally to a specific URI.
actingAs($user): Set the authenticated user before making a request. Sets a flag on the test kernel to inject the user into the auth system.
Request building: $this->get('/url') creates a GET Request. $this->post('/url', ['key' => 'val']) creates a POST request with the given body. $this->getJson('/api/url') adds Accept: application/json.
Headers in tests: $this->withHeaders(['Authorization' => "Bearer {$token}"]) — chainable headers added to the next request.
Code Example
<?php
namespace Framework\Testing;
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
private array $defaultHeaders = [];
private ?object $actingAsUser = null;
// HTTP testing methods
protected function get(string $uri, array $headers = []): TestResponse
{
return $this->call('GET', $uri, [], $headers);
}
protected function post(string $uri, array $data = [], array $headers = []): TestResponse
{
return $this->call('POST', $uri, $data, $headers);
}
protected function put(string $uri, array $data = [], array $headers = []): TestResponse
{
return $this->call('PUT', $uri, $data, $headers);
}
protected function patch(string $uri, array $data = [], array $headers = []): TestResponse
{
return $this->call('PATCH', $uri, $data, $headers);
}
protected function delete(string $uri, array $headers = []): TestResponse
{
return $this->call('DELETE', $uri, [], $headers);
}
protected function getJson(string $uri, array $headers = []): TestResponse
{
return $this->get($uri, array_merge(['Accept' => 'application/json'], $headers));
}
protected function postJson(string $uri, array $data = [], array $headers = []): TestResponse
{
return $this->post($uri, $data, array_merge([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
], $headers));
}
protected function actingAs(object $user): static
{
$this->actingAsUser = $user;
return $this;
}
protected function withHeaders(array $headers): static
{
$this->defaultHeaders = array_merge($this->defaultHeaders, $headers);
return $this;
}
private function call(string $method, string $uri, array $data = [], array $headers = []): TestResponse
{
$request = $this->buildRequest($method, $uri, $data, array_merge($this->defaultHeaders, $headers));
if ($this->actingAsUser !== null) {
// Bypass auth for this request by pre-injecting the user
static::$app->instance('test.user', $this->actingAsUser);
$this->actingAsUser = null;
}
$response = static::$app->make(\Framework\Http\Kernel::class)->handle($request);
return new TestResponse($response);
}
private function buildRequest(string $method, string $uri, array $data, array $headers): \Framework\Http\Request
{
$body = in_array($method, ['POST', 'PUT', 'PATCH'])
? (isset($headers['Content-Type']) && $headers['Content-Type'] === 'application/json'
? json_encode($data)
: http_build_query($data))
: '';
return \Framework\Http\Request::create($method, $uri, $headers, $body);
}
}
class TestResponse
{
public function __construct(private readonly \Framework\Http\Response $response) {}
public function assertStatus(int $code): static
{
\PHPUnit\Framework\Assert::assertEquals($code, $this->response->getStatus(),
"Expected status {$code}, got {$this->response->getStatus()}.");
return $this;
}
public function assertOk(): static { return $this->assertStatus(200); }
public function assertCreated(): static { return $this->assertStatus(201); }
public function assertNoContent(): static { return $this->assertStatus(204); }
public function assertNotFound(): static { return $this->assertStatus(404); }
public function assertForbidden(): static { return $this->assertStatus(403); }
public function assertUnauthorized(): static { return $this->assertStatus(401); }
public function assertJson(array $expected): static
{
$actual = json_decode($this->response->getBody(), true);
\PHPUnit\Framework\Assert::assertArrayContains($expected, $actual);
return $this;
}
public function assertSee(string $text): static
{
\PHPUnit\Framework\Assert::assertStringContainsString($text, $this->response->getBody());
return $this;
}
public function assertRedirect(?string $uri = null): static
{
$status = $this->response->getStatus();
\PHPUnit\Framework\Assert::assertTrue(
$status >= 300 && $status < 400,
"Expected redirect, got status {$status}."
);
if ($uri !== null) {
\PHPUnit\Framework\Assert::assertEquals($uri, $this->response->getHeader('location')[0] ?? '');
}
return $this;
}
}