Symfony 5 Api 测试 createClient() LogicalException
Symfony 5 Api Testing createClient() LogicalException
正如标题所说,我正在使用 Symfony 5 构建一个 API。我有一些控制器需要不同的用户权限,我想测试一下,所以我决定创建两个具有不同用户权限的用户用于测试目的的角色 - ROLE_USER
和 ROLE_ADMIN
。
当前的代码是这样的(注意,它不是完整的代码,只是一个虚拟的 example/starting 点)
ApiTestCase.php
<?php
namespace App\Tests;
use App\Entity\User;
use App\Tests\Http\RequestBuilder;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ApiTestCase extends WebTestCase
{
private static $userData = [
'firstName' => 'Pera',
'lastName' => 'Peric',
'email' => 'test.user@test.com',
'password' => 'test123',
'roles' => [
'ROLE_USER'
]
];
private static $adminUserData = [
'firstName' => 'Admin',
'lastName' => 'Adminovic',
'email' => 'admin.user@test.com',
'password' => 'admin123',
'roles' => [
'ROLE_ADMIN'
]
];
public static function createTestUser()
{
$res = RequestBuilder::create(self::createClient())
->setMethod('POST')
->setUri('/api/v1/security/register')
->setJsonContent(self::$userData)
->getResponse();
$data = $res->getJsonContent();
return $data['data'];
}
public static function createTestAdminUser()
{
$res = RequestBuilder::create(self::createClient())
->setMethod('POST')
->setUri('/api/v1/security/register')
->setJsonContent(self::$adminUserData)
->getResponse();
$data = $res->getJsonContent();
var_dump($data);
return $data['data'];
}
public static function deleteTestUser()
{
self::createClient()->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository(User::class)
->deleteUserByEmail(self::$userData['email']);
}
public static function deleteTestAdminUser()
{
self::createClient()->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository(User::class)
->deleteUserByEmail(self::$adminUserData['email']);
}
}
LocationControllerTest.php
namespace App\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class LocationControllerTest extends ApiTestCase
{
public static $user;
public static $adminUser;
public static function setUpBeforeClass()
{
self::$user = self::createTestUser();
self::$adminUser = self::createTestAdminUser();
}
public function testUsersExist()
{
// this is jut to test out an idea
$this->assertContains('ROLE_USER', self::$user['roles']);
$this->assertContains('ROLE_ADMIN', self::$adminUser['roles']);
}
public static function tearDownAfterClass()
{
self::deleteTestUser();
self::deleteTestAdminUser();
}
}
当我运行测试时(sunit
是别名php bin/phpunit
):
➜ skeleton master ✗ (*?) sunit --filter LocationController [10:12AM]
PHPUnit 7.5.17 by Sebastian Bergmann and contributors.
Testing Project Test Suite
E 1 / 1 (100%)
Time: 742 ms, Memory: 24.00 MB
There was 1 error:
1) App\Tests\LocationControllerTest::testUsersExist
LogicException: Booting the kernel before calling Symfony\Bundle\FrameworkBundle\Test\WebTestCase::createClient() is not supported, the kernel should only be booted once. in /Users/shkabo/code/skeleton/vendor/symfony/framework-bundle/Test/WebTestCase.php:44
Stack trace:
#0 /Users/shkabo/code/skeleton/tests/ApiTestCase.php(45): Symfony\Bundle\FrameworkBundle\Test\WebTestCase::createClient()
#1 /Users/shkabo/code/skeleton/tests/LocationControllerTest.php(16): App\Tests\ApiTestCase::createTestAdminUser()
#2 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/Framework/TestSuite.php(703): App\Tests\LocationControllerTest::setUpBeforeClass()
#3 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/Framework/TestSuite.php(746): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#4 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/TestRunner.php(652): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#5 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/Command.php(206): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, true)
#6 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/Command.php(162): PHPUnit\TextUI\Command->run(Array, true)
#7 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/phpunit(17): PHPUnit\TextUI\Command::main()
#8 /Users/shkabo/code/skeleton/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php(291): include('/Users/shkabo/c...')
#9 /Users/shkabo/code/skeleton/bin/phpunit(13): require('/Users/shkabo/c...')
#10 {main}
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
我理解错误,但似乎找不到 it/this 方法的解决方案。也很有可能我做错了。
在数据库中,创建了第一个用户,而当我尝试创建第二个用户时抛出此错误。
我想要实现的是我有(静态)方法可以调用并创建虚拟 user/location/etc。并在测试该特定控制器时使用它,并在完成该特定控制器的测试后从数据库中销毁it/delete它。
RequestBuilder
https://github.com/nebkam/fluent-test/blob/master/src/RequestBuilder.php
问题是因为您使用了 2 次 static::createClient()
,这将再次启动内核。为避免这种情况,您可以创建一个客户端,然后克隆它并使用一种方法修改一些参数并将客户端作为引用传递。
在这里你可以找到我昨天在 repo 中写的一个问题和我找到的解决方案
private static ?KernelBrowser $client = null;
protected static ?KernelBrowser $admin = null;
protected static ?KernelBrowser $user = null;
/**
* @throws \Exception
*/
public function setUp(): void
{
$this->resetDatabase();
if (null === self::$client) {
self::$client = static::createClient();
}
if (null === self::$admin) {
self::$admin = clone self::$client;
$this->createAuthenticatedClient(self::$admin, 'admin@api.com', 'password');
}
if (null === self::$user) {
self::$user = clone self::$client;
$this->createAuthenticatedClient(self::$user, 'user@api.com', 'password');
}
}
protected function createAuthenticatedClient(KernelBrowser &$client, string $username, string $password): void
{
$client->request(
'POST',
'/api/v1/login_check',
[
'_email' => $username,
'_password' => $password,
]
);
$data = \json_decode($client->getResponse()->getContent(), true);
$client->setServerParameter('HTTP_Authorization', \sprintf('Bearer %s', $data['token']));
$client->setServerParameter('CONTENT_TYPE', 'application/json');
}
我遇到了同样的问题,通过初始化 setUp()
中的 Client
解决了
protected function setUp(): void
{
$this->client = $this->makeClient();
$this->client->followRedirects();
...
}
并且在测试中我使用 $this->client->loginUser()
登录用户,将身份验证防火墙类型作为第二个参数
public function testWhateverYouNeed(): void
{
... // Get the user from fixtures
$this->client->loginUser($user, 'admin');
}
防火墙在config/packages/security.yaml
中定义
您可以根据官方建议在创建客户端之前调用self::ensureKernelShutdown()
。
self::ensureKernelShutdown();
$sally = static::createClient();
我不确定这是否能解决这个问题,但我遇到了类似的问题,Google 让我来到这里。
正如标题所说,我正在使用 Symfony 5 构建一个 API。我有一些控制器需要不同的用户权限,我想测试一下,所以我决定创建两个具有不同用户权限的用户用于测试目的的角色 - ROLE_USER
和 ROLE_ADMIN
。
当前的代码是这样的(注意,它不是完整的代码,只是一个虚拟的 example/starting 点)
ApiTestCase.php
<?php
namespace App\Tests;
use App\Entity\User;
use App\Tests\Http\RequestBuilder;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ApiTestCase extends WebTestCase
{
private static $userData = [
'firstName' => 'Pera',
'lastName' => 'Peric',
'email' => 'test.user@test.com',
'password' => 'test123',
'roles' => [
'ROLE_USER'
]
];
private static $adminUserData = [
'firstName' => 'Admin',
'lastName' => 'Adminovic',
'email' => 'admin.user@test.com',
'password' => 'admin123',
'roles' => [
'ROLE_ADMIN'
]
];
public static function createTestUser()
{
$res = RequestBuilder::create(self::createClient())
->setMethod('POST')
->setUri('/api/v1/security/register')
->setJsonContent(self::$userData)
->getResponse();
$data = $res->getJsonContent();
return $data['data'];
}
public static function createTestAdminUser()
{
$res = RequestBuilder::create(self::createClient())
->setMethod('POST')
->setUri('/api/v1/security/register')
->setJsonContent(self::$adminUserData)
->getResponse();
$data = $res->getJsonContent();
var_dump($data);
return $data['data'];
}
public static function deleteTestUser()
{
self::createClient()->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository(User::class)
->deleteUserByEmail(self::$userData['email']);
}
public static function deleteTestAdminUser()
{
self::createClient()->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository(User::class)
->deleteUserByEmail(self::$adminUserData['email']);
}
}
LocationControllerTest.php
namespace App\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class LocationControllerTest extends ApiTestCase
{
public static $user;
public static $adminUser;
public static function setUpBeforeClass()
{
self::$user = self::createTestUser();
self::$adminUser = self::createTestAdminUser();
}
public function testUsersExist()
{
// this is jut to test out an idea
$this->assertContains('ROLE_USER', self::$user['roles']);
$this->assertContains('ROLE_ADMIN', self::$adminUser['roles']);
}
public static function tearDownAfterClass()
{
self::deleteTestUser();
self::deleteTestAdminUser();
}
}
当我运行测试时(sunit
是别名php bin/phpunit
):
➜ skeleton master ✗ (*?) sunit --filter LocationController [10:12AM]
PHPUnit 7.5.17 by Sebastian Bergmann and contributors.
Testing Project Test Suite
E 1 / 1 (100%)
Time: 742 ms, Memory: 24.00 MB
There was 1 error:
1) App\Tests\LocationControllerTest::testUsersExist
LogicException: Booting the kernel before calling Symfony\Bundle\FrameworkBundle\Test\WebTestCase::createClient() is not supported, the kernel should only be booted once. in /Users/shkabo/code/skeleton/vendor/symfony/framework-bundle/Test/WebTestCase.php:44
Stack trace:
#0 /Users/shkabo/code/skeleton/tests/ApiTestCase.php(45): Symfony\Bundle\FrameworkBundle\Test\WebTestCase::createClient()
#1 /Users/shkabo/code/skeleton/tests/LocationControllerTest.php(16): App\Tests\ApiTestCase::createTestAdminUser()
#2 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/Framework/TestSuite.php(703): App\Tests\LocationControllerTest::setUpBeforeClass()
#3 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/Framework/TestSuite.php(746): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#4 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/TestRunner.php(652): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#5 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/Command.php(206): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, true)
#6 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/src/TextUI/Command.php(162): PHPUnit\TextUI\Command->run(Array, true)
#7 /Users/shkabo/code/skeleton/bin/.phpunit/phpunit-7.5-0/phpunit(17): PHPUnit\TextUI\Command::main()
#8 /Users/shkabo/code/skeleton/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php(291): include('/Users/shkabo/c...')
#9 /Users/shkabo/code/skeleton/bin/phpunit(13): require('/Users/shkabo/c...')
#10 {main}
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
我理解错误,但似乎找不到 it/this 方法的解决方案。也很有可能我做错了。
在数据库中,创建了第一个用户,而当我尝试创建第二个用户时抛出此错误。
我想要实现的是我有(静态)方法可以调用并创建虚拟 user/location/etc。并在测试该特定控制器时使用它,并在完成该特定控制器的测试后从数据库中销毁it/delete它。
RequestBuilder
https://github.com/nebkam/fluent-test/blob/master/src/RequestBuilder.php
问题是因为您使用了 2 次 static::createClient()
,这将再次启动内核。为避免这种情况,您可以创建一个客户端,然后克隆它并使用一种方法修改一些参数并将客户端作为引用传递。
在这里你可以找到我昨天在 repo 中写的一个问题和我找到的解决方案
private static ?KernelBrowser $client = null;
protected static ?KernelBrowser $admin = null;
protected static ?KernelBrowser $user = null;
/**
* @throws \Exception
*/
public function setUp(): void
{
$this->resetDatabase();
if (null === self::$client) {
self::$client = static::createClient();
}
if (null === self::$admin) {
self::$admin = clone self::$client;
$this->createAuthenticatedClient(self::$admin, 'admin@api.com', 'password');
}
if (null === self::$user) {
self::$user = clone self::$client;
$this->createAuthenticatedClient(self::$user, 'user@api.com', 'password');
}
}
protected function createAuthenticatedClient(KernelBrowser &$client, string $username, string $password): void
{
$client->request(
'POST',
'/api/v1/login_check',
[
'_email' => $username,
'_password' => $password,
]
);
$data = \json_decode($client->getResponse()->getContent(), true);
$client->setServerParameter('HTTP_Authorization', \sprintf('Bearer %s', $data['token']));
$client->setServerParameter('CONTENT_TYPE', 'application/json');
}
我遇到了同样的问题,通过初始化 setUp()
Client
解决了
protected function setUp(): void
{
$this->client = $this->makeClient();
$this->client->followRedirects();
...
}
并且在测试中我使用 $this->client->loginUser()
登录用户,将身份验证防火墙类型作为第二个参数
public function testWhateverYouNeed(): void
{
... // Get the user from fixtures
$this->client->loginUser($user, 'admin');
}
防火墙在config/packages/security.yaml
您可以根据官方建议在创建客户端之前调用self::ensureKernelShutdown()
。
self::ensureKernelShutdown();
$sally = static::createClient();
我不确定这是否能解决这个问题,但我遇到了类似的问题,Google 让我来到这里。