Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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

api/tests/ProjectResourceTest.php
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 curlsetDefaultOptions 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.