Test Data and Authentication in Tests
Test Data and Authentication in Tests
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Chapter 93's testCreateProject FAILED for lack of a token – this chapter adds A test user AND a VALID JWT, SO authenticated endpoints BECOME TESTABLE.
Reusing Doctrine fixtures for tests
The EXACT AppFixtures from chapter 15 can ALSO be loaded in tests – a SEPARATE, ISOLATED test database (.env.test, EXACTLY as mentioned in chapter 93) prevents tests from CHANGING the DEVELOPMENT data from chapter 29 (45 test projects).
Generating a token for tests
private function createAuthenticatedClient(): \ApiPlatform\Symfony\Bundle\Test\Client
{
$client = static::createClient();
$container = static::getContainer();
$user = new \App\Entity\User();
$user->setEmail('test@example.com');
$user->setPassword(
$container->get('security.user_password_hasher')->hashPassword($user, 'test1234'),
);
$entityManager = $container->get('doctrine')->getManager();
$entityManager->persist($user);
$entityManager->flush();
$response = $client->request('POST', '/api/login', [
'json' => ['email' => 'test@example.com', 'password' => 'test1234'],
]);
$token = $response->toArray()['token'];
$client->setDefaultOptions(['headers' => ['Authorization' => "Bearer {$token}"]]);
return $client;
}EXACTLY the login flow from chapter 49, now GONE THROUGH PROGRAMMATICALLY instead of via curl – setDefaultOptions ENSURES that ALL subsequent requests from THIS client are AUTOMATICALLY authenticated, EXACTLY like the interceptor from chapter 77 on the React side.
Adjusting the test
public function testCreateProject(): void
{
$client = $this->createAuthenticatedClient();
$client->request('POST', '/api/projects', [
'json' => ['name' => 'Test project'],
'headers' => ['Content-Type' => 'application/ld+json'],
]);
self::assertResponseStatusCodeSame(201);
}Achtung: Creating a NEW test user PER test is SLOW (password hashing, chapter 48, is DELIBERATELY compute-intensive) – LARGER test suites move this logic into a setUp() method OR a REUSABLE trait, to avoid repeating it in EVERY test.
Testing the voter (chapters 52-53)
public function testCannotDeleteOthersProject(): void
{
$ownerClient = $this->createAuthenticatedClient();
$response = $ownerClient->request('POST', '/api/projects', [
'json' => ['name' => 'Someone elses project'],
]);
$projectIri = $response->toArray()['@id'];
$otherClient = $this->createAuthenticatedClient();
$otherClient->request('DELETE', $projectIri);
self::assertResponseStatusCodeSame(403);
}TWO SEPARATE authenticated clients simulate TWO DIFFERENT users – THIS test verifies EXACTLY the voter logic from chapter 52 ($project->getOwner() === $user) AUTOMATICALLY, instead of recreating it MANUALLY with two curl sessions.
Tipp: THIS exact test WOULD HAVE FAILED IMMEDIATELY when the voter was first written (chapter 52), had getOwner() been FORGOTTEN – AUTOMATED tests catch EXACTLY SUCH regression bugs, BEFORE they LAND in production.