Magento 2 Experts — Hyvä Theme, Tailwind CSS & SEO from a Single Source ›

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 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.