使用注解在 Symfony 4.4 中测试控制器
Testing Controllers in Symfony 4.4 while using annotations
我在使用 FOSRestBundle 和 JMSSserializer 在 Symfony 4.4 中测试我的控制器时遇到了问题。我的控制器非常简单,通常只包含对其他服务的调用,但我使用的是 ParamConverter、Serializer、Deserializer 等。我不确定返回的字段是否符合我的预期。
我想测试 serialization/deserialization 如何处理我的实体。每当我在实体中添加字段或更改字段组时,测试都应该失败。
理想情况下,我会模拟我的服务并直接调用 Action,但我找不到任何地方,如何调用所有注释都触发的 Action 方法。
除了对整个请求进行功能测试之外,还有其他测试方法吗?
我要测试的控制器动作:
/**
* @Rest\Post("/entity")
* @Rest\Put("/entity/{entityId<\d+>?}")
* @ParamConverter(name="entity", converter="app.request_body",options={
* "deserializationContext"={"groups"={
* "DetailsGroup",
* "nested"={"IdGroup"},
* "owner"={"IdGroup"}
* }}
* }
* )
* @Rest\View(serializerGroups={"IdGroup"}, statusCode=Response::HTTP_CREATED)
* @param int|null $entityId
* @param Entity $entity
* @param ConstraintViolationListInterface $validationErrors
* @return Entity
* @throws InvalidArgumentException
* @throws Exception
*/
public function setEntityAction(?int $entityId, Entity $entity, ConstraintViolationListInterface $validationErrors): Entity
{
if ($validationErrors->count() > 0) {
throw new InvalidArgumentException('...');
}
return $this->entityService->setEntity($entity, $this->getUser());
}
当您要考虑要覆盖的注释时,测试控制器通常需要进行大量设置。通过单元测试进行设置,您只需实例化控制器并模拟调用的服务是不够的。
您可以做的是使用 Symfony 的 WebTestCase 来 运行 通过启动的应用程序内核进行功能测试。这几乎是在一个设置中测试您的控制器,该设置非常类似于在您的应用程序中实际调用它时会发生什么。这样做的缺点是,它还会 运行 所有服务。
您仍然可以尝试一些解决方法。您可以直接在正在使用的服务容器中替换控制器中调用的服务。通过更改测试中的容器或通过提供自定义 config/services_test.yaml
将服务替换为 "NullService":
# config/services_test.yaml
services:
App\Service\MyEntityService: # This is the original class name
class: App\Service\NullService # This is the class name of the "null" service
这样,只要你注入 MyEntityService
,你就会得到 NullService
。这将要求您扩展原始服务或拥有一个它们都可以实现的接口。如果您有接口,您可能希望将其用作服务 ID 而不是原始 class 名称。
这种方法的缺点是,您必须手动连接每个服务,并且必须为其创建虚拟替换。好处是,您可以很容易地 return 在控制实施时获得所需的数据。
另一种方法是更改测试本身的容器:
protected function testMyController(): void
{
$kernel = self::bootKernel();
$mock = $this->createMock(MyEntityService::class);
$kernel->getContainer()->set(MyEntityService::class, $mock);
}
我在使用 FOSRestBundle 和 JMSSserializer 在 Symfony 4.4 中测试我的控制器时遇到了问题。我的控制器非常简单,通常只包含对其他服务的调用,但我使用的是 ParamConverter、Serializer、Deserializer 等。我不确定返回的字段是否符合我的预期。
我想测试 serialization/deserialization 如何处理我的实体。每当我在实体中添加字段或更改字段组时,测试都应该失败。
理想情况下,我会模拟我的服务并直接调用 Action,但我找不到任何地方,如何调用所有注释都触发的 Action 方法。
除了对整个请求进行功能测试之外,还有其他测试方法吗?
我要测试的控制器动作:
/**
* @Rest\Post("/entity")
* @Rest\Put("/entity/{entityId<\d+>?}")
* @ParamConverter(name="entity", converter="app.request_body",options={
* "deserializationContext"={"groups"={
* "DetailsGroup",
* "nested"={"IdGroup"},
* "owner"={"IdGroup"}
* }}
* }
* )
* @Rest\View(serializerGroups={"IdGroup"}, statusCode=Response::HTTP_CREATED)
* @param int|null $entityId
* @param Entity $entity
* @param ConstraintViolationListInterface $validationErrors
* @return Entity
* @throws InvalidArgumentException
* @throws Exception
*/
public function setEntityAction(?int $entityId, Entity $entity, ConstraintViolationListInterface $validationErrors): Entity
{
if ($validationErrors->count() > 0) {
throw new InvalidArgumentException('...');
}
return $this->entityService->setEntity($entity, $this->getUser());
}
当您要考虑要覆盖的注释时,测试控制器通常需要进行大量设置。通过单元测试进行设置,您只需实例化控制器并模拟调用的服务是不够的。
您可以做的是使用 Symfony 的 WebTestCase 来 运行 通过启动的应用程序内核进行功能测试。这几乎是在一个设置中测试您的控制器,该设置非常类似于在您的应用程序中实际调用它时会发生什么。这样做的缺点是,它还会 运行 所有服务。
您仍然可以尝试一些解决方法。您可以直接在正在使用的服务容器中替换控制器中调用的服务。通过更改测试中的容器或通过提供自定义 config/services_test.yaml
将服务替换为 "NullService":
# config/services_test.yaml
services:
App\Service\MyEntityService: # This is the original class name
class: App\Service\NullService # This is the class name of the "null" service
这样,只要你注入 MyEntityService
,你就会得到 NullService
。这将要求您扩展原始服务或拥有一个它们都可以实现的接口。如果您有接口,您可能希望将其用作服务 ID 而不是原始 class 名称。
这种方法的缺点是,您必须手动连接每个服务,并且必须为其创建虚拟替换。好处是,您可以很容易地 return 在控制实施时获得所需的数据。
另一种方法是更改测试本身的容器:
protected function testMyController(): void
{
$kernel = self::bootKernel();
$mock = $this->createMock(MyEntityService::class);
$kernel->getContainer()->set(MyEntityService::class, $mock);
}