在每个测试在功能测试中无法通过身份验证之前重置数据库

问题描述

我正在为我的REST-Api进行功能测试。 Api受授权保护。为此,我选择了json_login提供程序。到现在为止还挺好。在正常环境中通过失眠进行访问时,身份验证才有效。

现在我需要功能测试。为此,我通过配置的User-class创建一个用户并将其保留在数据库中。可以正常工作。 但是当然,该测试仅能运行一次,因为用户已经在以下测试中存在。

因此,我尝试了hautelook/alice-bundle的ResetDatabaseTrait或ReloadDatabaseTrait以及dmaicher/doctrine-test-bundle。 两者都表现出相同的行为:身份验证器找不到新创建的用户。 (EntityUserProvider::loadUserByUsername未找到用户

很显然,EntityUserProvider似乎在数据库中使用了不同的“连接”,无法查看那些库启动的事务。

在我的测试中,负责持久保存用户的实体管理器是通过以下方式创建的:

protected function setUp(): void {
    $kernel = self::bootKernel();
    $this->em = $kernel->getContainer()
        ->get('doctrine')
        ->getManager();
}

或直接在创建用户之前

$em = self::$container->get('doctrine')->getManager();

这对我来说似乎是正确的。但是无论如何,我都会得到相同的结果->“无效的凭据”,因为找不到用户

也许有人可以指出我正确的方向?

解决方法

休息片刻后,我记得创建测试时的一个细节。所有示例都不需要其中带有self:bootKernel()的setUp-Method。但是没有它,self::$container属性为空,因此我将其添加到测试类中。也许有解决问题的方法?

我是对的! 我正在使用Api-Platform软件包。因此,我的测试班基于ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase。该类没有setUp方法,但是检查createClient()时发现,通过调用bootKernel()创建了内核,该内核也停止了所有正在运行的内核。

所以我的setUp()方法创建了一个内核。使用该内核,我创建了我的用户。 然后,我致电createClient()为请求创建测试客户端。这杀死了我最初的内核,并创建了一个新内核,然后导致了问题。

重新排列语句-首先创建客户端,然后从现在创建的容器中获取EntityManager,并在创建客户端解决问题后创建User。

,

两天后,呵呵

当你想调用多个请求时,例如,如果你想要第一个请求你得到令牌,第二个你用这个令牌调用并检查身份验证,在这个调用中,如果你使用 use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait trait 你的数据库第一次调用后休息,您有令牌但数据库为空,第二次调用失败。 因此,请再次阅读文档的这一重要部分:

但是有一个警告:在某些测试中,有必要在一个测试中执行多个请求,例如当通过 API 创建用户并检查使用相同密码进行后续登录时。但是,默认情况下客户端将重新启动内核,这将重置数据库。您可以通过添加 $client->disableReboot(); 来防止这种情况发生。到这样的测试。 Writing Functional Tests

我知道我们是懒惰的开发人员,而且首先阅读的是代码,而不是文档 :-)

$client = static::createClient();
$client->disableReboot();

$manager = self::getContainer()->get('doctrine')->getManager();
$user = new User();
$user->setEmail('user@example.com');
$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();