如何在Symfony 2.8+PHPUnit中测试对象是否已持久化到数据库
当然有办法啦!针对你用Symfony 2.8 + PHPUnit的场景,我给你两个实用的方案,哪怕不看那200行的函数代码也能搞定:
方案1:用Doctrine EntityManager模拟(单元测试首选)
如果是纯单元测试,我们不需要真的碰数据库,只要验证函数有没有正确触发Doctrine的持久化方法就行。核心思路是给函数注入一个模拟的EntityManager,然后检查它的persist()和flush()方法是否被调用:
- 用PHPUnit的
MockBuilder创建EntityManager的mock对象 - 预设对
persist()和flush()方法的调用期望 - 调用目标函数后,PHPUnit会自动验证这些期望是否满足
举个代码例子:
public function testInvoiceIsPersisted() { // 创建EntityManager的模拟对象 $entityManager = $this->createMock(\Doctrine\ORM\EntityManager::class); // 期望persist方法被调用1次,且参数是Invoice类的实例 $entityManager->expects($this->once()) ->method('persist') ->with($this->isInstanceOf(Invoice::class)); // 期望flush方法也被调用1次 $entityManager->expects($this->once()) ->method('flush'); // 把mock注入到你的服务/类里(假设你的函数属于这个服务) $yourService = new YourBusinessService($entityManager); // 构造测试用的Invoice对象 $testInvoice = new Invoice(); // 可以给invoice设置必要的属性,比如编号、金额等 // 调用你要测试的那个持久化函数 $yourService->yourLongPersistFunction($testInvoice); }
这个方案速度超快,完全在内存中运行,适合快速验证逻辑是否正确触发了持久化操作。
方案2:功能测试(验证真实数据库写入)
如果你想确认数据真的被写到数据库里(比如要验证Doctrine映射、数据库约束是否生效),可以用Symfony的KernelTestCase来做集成测试:
- 启动Symfony内核,拿到真实的EntityManager
- 调用目标函数后,直接从数据库查询对应的Invoice记录
- 断言记录存在且属性符合预期
代码示例:
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; class InvoiceDatabasePersistenceTest extends KernelTestCase { private $entityManager; protected function setUp() { // 启动Symfony内核 $kernel = self::bootKernel(); // 获取真实的EntityManager $this->entityManager = $kernel->getContainer() ->get('doctrine') ->getManager(); } public function testInvoiceIsCreatedInDatabase() { // 创建一个带唯一标识的测试Invoice,方便后续查询 $testInvoice = new Invoice(); $testInvoice->setInvoiceNumber('TEST-INV-2024-001'); // 记录初始的Invoice数量 $initialCount = $this->entityManager->getRepository(Invoice::class)->count([]); // 获取你的服务并调用目标函数 $yourService = self::$container->get(YourBusinessService::class); $yourService->yourLongPersistFunction($testInvoice); // 清除EntityManager缓存,确保从数据库重新读取数据 $this->entityManager->clear(); // 从数据库查询刚才的测试Invoice $savedInvoice = $this->entityManager->getRepository(Invoice::class) ->findOneBy(['invoiceNumber' => 'TEST-INV-2024-001']); // 断言记录存在 $this->assertNotNull($savedInvoice); // 断言数量增加了1 $this->assertEquals($initialCount + 1, $this->entityManager->getRepository(Invoice::class)->count([])); // 进一步断言属性匹配 $this->assertEquals('TEST-INV-2024-001', $savedInvoice->getInvoiceNumber()); } protected function tearDown() { parent::tearDown(); // 清理测试数据,避免影响其他测试用例 $testInvoice = $this->entityManager->getRepository(Invoice::class) ->findOneBy(['invoiceNumber' => 'TEST-INV-2024-001']); if ($testInvoice) { $this->entityManager->remove($testInvoice); $this->entityManager->flush(); } $this->entityManager->close(); $this->entityManager = null; } }
这个方案会真实操作数据库,适合验证完整的流程。记得在tearDown里清理测试数据,或者也可以在setUp里开启事务,tearDown里回滚,这样更高效。
额外小提示
- 如果你的函数是控制器里的方法,也可以用
WebTestCase模拟HTTP请求,之后再查询数据库验证结果 - Symfony 2.8对应的PHPUnit版本是4.x或5.x,语法上和新版本略有差异,但核心思路完全一致
- 如果函数里还有其他依赖(比如日志、其他服务),可以同样用mock来隔离,只关注持久化相关的逻辑
内容的提问来源于stack exchange,提问作者Tibo
相关产品推荐
相关产品推荐

