Symfony5功能测试类中如何正确获取EntityManager实例?
问题原因
这个报错是因为WebTestCase的createClient()方法会自动完成内核启动逻辑,你在setUp()中提前手动调用self::bootKernel()相当于重复启动内核,违反了Symfony测试中内核仅允许启动一次的限制。
解决方案
调整EntityManager的获取逻辑,改成懒加载模式,优先复用createClient()已经启动的内核,只有内核未启动时才手动启动:
第一步:修改AbstractWebTestCase
abstract class AbstractWebTestCase extends WebTestCase { private $entityManager; public function getEntityManager() { if (null === $this->entityManager) { // 优先复用createClient已经启动的内核 $kernel = static::$kernel ?? self::bootKernel(); $this->entityManager = $kernel->getContainer()->get('doctrine')->getManager(); } return $this->entityManager; } // 创建认证用户的便捷方法无需修改 protected function createAuthenticatedUser($username, $password) { $client = static::createClient(); $client->request('POST', '/api/login', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode(['username' => $username,'password' => $password,])); $data = json_decode($client->getResponse()->getContent(), true); $client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $data['token'])); return $client; } protected function setUp(): void { parent::setUp(); // 删掉原有手动启动内核的逻辑 } protected function tearDown(): void { parent::tearDown(); if (null !== $this->entityManager) { $this->entityManager->close(); $this->entityManager = null; } } }
第二步:测试中使用EntityManager
在调用createAuthenticatedUser()(也就是完成内核启动)后,再调用getEntityManager()获取实例即可:
class FooControllerTest extends AbstractWebTestCase { public function testFoo(): void { $client = $this->createAuthenticatedUser('foo@foo.com', 'pwd'); $client->request('POST', '/api/uri/foo', [], [], ['CONTENT_TYPE' => 'application/json'], '{...}'); $this->assertResponseIsSuccessful(); // 获取EntityManager执行数据库校验 $em = $this->getEntityManager(); $fooRecord = $em->getRepository(Foo::class)->findOneBy(['field' => '预期值']); $this->assertNotNull($fooRecord); } }
这个方案兼容两种测试场景:需要发起HTTP请求的功能测试,以及不需要发起请求、仅需要操作数据库的测试,都不会出现内核重复启动的问题。
内容的提问来源于stack exchange,提问作者automatix
相关产品推荐
相关产品推荐

