Symfony Doctrine在PHPUnit测试中异常:eup字段存入数据库为空
技术栈
- Symfony v6.4.8
- doctrine/orm 2.19.5
- phpunit/phpunit 9.6.19
场景描述
在Symfony中构建了REST API GET端点/api/contract/{$id},该端点会请求外部系统API获取数据,结合本地数据库数据后,在本地MSSQL数据库创建新记录。其中eup字段在数据库中定义为nvarchar(10)类型。
核心保存代码
private function saveContract(EntityManager $em, array $data): ContractData { $contract = new ContractData(); // [... 其他变量赋值] $contract->setEup($data['eup']); // 示例值: "2024-06-29" $em->persist($contract); $em->flush(); return $contract; }
PHPUnit测试代码
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class ContractDataControllerTest extends WebTestCase { public static function contractProvider(): array { $testCases = array( ['18fac35b-4afe-476e-9094-1437deeb6f0a', 'stringValue1', '2024-06-29'], ); return $testCases; } /** * @covers \App\Controller\API\ContractDataController::contract * @dataProvider contractProvider */ public function testGetContractWhileLoggedIn($id, $expectValue1, $expectEup): void { $uri = $this->getUriWithSignature('/api/contract/', $id); $client = static::createClient(); $client->disableReboot(); $client->setServerParameter('HTTP_Authorization', 'Bearer ' . $_ENV['TOKEN']); $client->request('GET', $uri); $this->assertResponseStatusCodeSame(200); $responseData = json_decode($client->getResponse()->getContent(), true); $this->assertSame($expectValue1, $responseData['value1']); // 其他字段测试正常,仅eup字段在数据库中为空 $this->assertSame($expectEup, $responseData['valueEup']); } }
问题现象
- Postman调用API时,记录能正确创建,
eup字段会被填入预期字符串; - 运行PHPUnit测试
php bin/phpunit --filter myTest时,记录能创建,但eup字段为NULL。
已做排查
- 测试中其他字段均能正确存储;
- 该字段的值可写入其他字段,排除值本身的格式/长度问题;
- Xdebug确认:执行
$em->persist($contract);时$contract->eup已被正确赋值为字符串,但flush()后数据库中该字段为NULL; - 添加Doctrine日志后发现,测试环境生成的SQL INSERT语句缺失
eup字段:Executing statement: INSERT INTO contract_data (contract_number) VALUES (?) (parameters: array{"1":"XYZ123456"}, types: array{"1":2})
可能的原因及解决方向
Doctrine元数据缓存未更新
测试环境可能缓存了旧的实体元数据,导致新添加的eup字段未被识别。执行以下命令清除测试环境缓存:php bin/console cache:clear --env=test也可以在phpunit配置中禁用元数据缓存,确保每次测试都读取最新的实体映射。
实体映射配置不一致
检查ContractData实体的映射(注释/XML/YAML文件),确认测试环境下的映射是否包含eup字段。有可能测试环境使用了独立的映射配置文件,或者映射定义中遗漏了该字段,导致Doctrine生成SQL时忽略它。测试环境数据库Schema未同步
测试环境的数据库表结构可能未同步最新的变更,比如eup字段是新增的,但测试库中还没有该字段,或者字段配置与生产环境不一致。可以运行以下命令同步测试库Schema:php bin/console doctrine:schema:update --force --env=test测试环境的外部API Mock数据异常
测试时如果Mock了外部系统API的响应,需要确认Mock返回的数据中包含正确的eup值。虽然Xdebug看到$contract->eup已赋值,但要验证这个值确实来自外部API的Mock响应,而非测试代码中的硬编码遗漏。实体Setter方法隐藏逻辑
虽然Postman调用正常,但要再次确认setEup方法没有特殊逻辑(比如条件赋值、数据转换错误),且测试环境下的实体类代码与生产环境一致,不存在分支代码差异导致值被清空的情况。
内容的提问来源于stack exchange,提问作者SvenW

