Reputation: 371
I am implementing functional tests for my REST-Api. The Api is protected by authorization. For this I chose the json_login provider. So far, so good. Authentication works when accessing in the normal environment via Insomnia.
Now I want functional tests. For that, I create an user via the configured User-class and persist it in the database. Works as expected. But of course the test only works once as the user already exists in the following tests.
So I tried hautelook/alice-bundle
with ResetDatabaseTrait or ReloadDatabaseTrait as well as dmaicher/doctrine-test-bundle
.
Both show the same behaviour: The authenticator can not find the newly created user. (EntityUserProvider::loadUserByUsername
finds no user)
Apparently the EntityUserProvider seems to use a different "connection" into the database that can not look into the transaction those libraries started.
The entity-manager in my test that is responsible for persisting my user is created either with
protected function setUp(): void {
$kernel = self::bootKernel();
$this->em = $kernel->getContainer()
->get('doctrine')
->getManager();
}
or directly before creating the user with
$em = self::$container->get('doctrine')->getManager();
which seems correct for me. But in any case I get the same result -> "Invalid credentials" because the user can not be found.
Maybe someone out there can point me into the right direction?
Upvotes: 1
Views: 1488
Reputation: 712
After two days , hooh
when you want to call multiple request, for example if you want at first request you get token and the second you call with this token and check auth, in during this calls if you use use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait
trait your data base rest after first call, you have token but database is empty, and second call fail.
So, read again this important part of documentation :
There is one caveat though: in some tests, it is necessary to perform multiple requests in one test, for example when creating a user via the API and checking that a subsequent login using the same password works. However, the client will by default reboot the kernel, which will reset the database. You can prevent this by adding $client->disableReboot(); to such tests. Writing Functional Tests
I, know we are lazy developer and first read code, not document :-)
$client = static::createClient();
$client->disableReboot();
$manager = self::getContainer()->get('doctrine')->getManager();
$user = new User();
$user->setEmail('[email protected]');
$user->setPassword(
self::getContainer()->get('security.user_password_hasher')->hashPassword($user, $password = 'pass1234')
);
$manager->persist($user);
$manager->flush();
$response = $client->request('POST', '/authentication-token', [
'headers' => ['Content-Type' => 'application/json'],
'json' => [
'email' => $user->getEmail(),
'password' => $password ,
],
]);
$token = $response->toArray()['token'] ?? null;
$client->request('GET', '/profile', [
'auth_bearer' => $token
]);
self::assertResponseIsSuccessful();
Upvotes: 2
Reputation: 371
After a refreshing break I remembered a detail when I was creating my tests. All the examples did not need a setUp-Method with self:bootKernel() in it. But without it the self::$container
property was empty, so I added that to my test-class. Maybe there was the solution to the problem?
I was right!
I am using the Api-Platform
package. Therefore my test-class is based in ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase
. That class does not have a setUp Method, but inspecting createClient()
I noticed that there the kernel is created by calling bootKernel()
which also stops any running kernel.
So my setUp() method created a kernel. With that kernel I created my user.
Then I called createClient()
to create the test-client for the requests. This killed my initial kernel and creates a new one which then leads to the problems.
Rearranging the statements - first create the client, then get the EntityManager from the now created container and create the User after creating the client solved the problem.
Upvotes: 2