如何用TDD测试Symfony路由返回的可反序列化产品数组JSON?
解决Symfony路由TDD测试中JSON结构验证问题
问题原因
你当前的测试逻辑存在漏洞:Symfony Serializer默认采用宽松模式,当JSON结构与Product实体不匹配时(比如返回["foo"]字符串数组),它仍会创建空的Product实例,导致assertContainsOnlyInstancesOf断言通过,但这并不是你要验证的有效产品数据。
解决方案
1. 反序列化前先校验JSON结构
先把响应内容解码为原生数组,直接校验每个元素的结构是否符合Product的字段要求,避免无效数据进入反序列化步骤。
示例代码:
// 先解码JSON为数组 $responseData = json_decode($content, true); $this->assertIsArray($responseData, '响应不是数组格式'); // 遍历每个元素,校验必要字段是否存在 foreach ($responseData as $item) { $this->assertIsArray($item, '数组元素不是对象结构'); $this->assertArrayHasKey('id', $item, '产品缺少id字段'); $this->assertArrayHasKey('name', $item, '产品缺少name字段'); $this->assertArrayHasKey('price', $item, '产品缺少price字段'); // 可根据你的Product实体添加更多必要字段校验 } // 之后再执行反序列化和实例校验 $products = $serializer->deserialize($content, 'App\Entity\Product[]', 'json'); $this->assertContainsOnlyInstancesOf(Product::class, $products);
2. 配置Serializer为严格模式
让Serializer在JSON结构不匹配时直接抛出异常,从根源上阻止无效对象的创建。你可以在测试中自定义Serializer实例,启用严格校验:
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; // 创建带严格校验的Serializer $normalizer = new ObjectNormalizer( propertyTypeExtractor: new ReflectionExtractor(), defaultContext: [ ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => false, // 启用类型强制校验 ObjectNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, // 禁止额外属性 ] ); $serializer = new Serializer([$normalizer], [new JsonEncoder()]); try { $products = $serializer->deserialize($content, 'App\Entity\Product[]', 'json'); } catch (\Symfony\Component\Serializer\Exception\ExceptionInterface $e) { $this->fail('JSON结构不符合Product数组要求: ' . $e->getMessage()); } $this->assertContainsOnlyInstancesOf(Product::class, $products);
3. 结合Validator组件验证对象有效性
即使反序列化成功,也要验证Product实例的业务规则(比如字段非空、格式正确),这完全符合TDD的测试规范——TDD不仅要验证格式,还要验证业务逻辑的正确性。
示例代码:
use Symfony\Component\Validator\Validation; // 初始化Validator $validator = Validation::createValidatorBuilder() ->enableAnnotationMapping() // 如果用注解验证规则 ->getValidator(); $products = $serializer->deserialize($content, 'App\Entity\Product[]', 'json'); $this->assertContainsOnlyInstancesOf(Product::class, $products); // 验证每个Product实例的有效性 foreach ($products as $product) { $violations = $validator->validate($product); $this->assertCount(0, $violations, '产品数据不符合业务规则: ' . (string)$violations); }
总结
优先在反序列化前校验JSON结构,再配合严格模式的Serializer和Validator组件,就能全面验证路由返回的JSON是否符合Product数组的格式和业务要求,完美解决你当前的测试漏洞。
内容的提问来源于stack exchange,提问作者Yannick
相关产品推荐
相关产品推荐

