You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 04:15:05