功能测试中的 Symfony 4 Mock 服务
Symfony 4 Mock service in functional test
我正在测试一项服务,该服务主要是序列化对象并通过服务将其发送到外部系统。
如果我创建典型的单元测试,我会模拟序列化程序和服务的响应,它与外部系统联系。事实上,除了在我的对象中调用一堆 setter 方法之外,没有太多要测试的了。
备选方案是使用 KernelTestCase 并创建功能测试,这很好,除非我不想联系外部系统,而是仅为此 "external" 服务使用模拟。
有没有可能在 Symfony 4 中实现这个?
或者还有其他方法吗?
我现在做的是:
<?php
namespace App\Tests\Service;
use App\Service\MyClassService;
use App\Service\ExternalClient\ExternalClient;
use JMS\Serializer\Serializer;
use JMS\Serializer\SerializerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;
class MyClassServiceTest extends KernelTestCase
{
/** @var LoggerInterface */
private $logger;
/** @var Serializer */
private $serializer;
/** @var ExternalClient */
private $externalClient;
/** @var RequestInterface */
private $request;
/** @var MyClassService */
private $myClassService;
public function setUp()
{
$kernel = self::bootKernel();
$this->logger = $kernel->getContainer()->get(LoggerInterface::class);
$this->serializer = $kernel->getContainer()->get(SerializerInterface::class);
$this->externalClient = $this->createMock(ExternalClient::class);
}
public function testPassRegistrationData()
{
$getParams = [
'amount' => '21.56',
'product_id' => 867,
'order_id' => '47t34g',
'order_item_id' => 2,
'email' => 'kiki%40bubu.com',
];
$this->generateMyClassService($getParams);
$userInformation = $this->myClassService->passRegistrationData();
var_dump($userInformation);
}
/**
* generateMyClassService
*
* @param $getParams
*
* @return MyClass
*/
private function generateMyClassService($getParams)
{
$this->request = new Request($getParams, [], [], [], [], [], null);
$this->myClassService = new MyClassService(
$this->logger,
$this->serializer,
$this->externalClient,
$this->request
);
}
}
返回此错误:
Symfony\Component\DependencyInjection\Exception\RuntimeException: Cannot autowire service "App\Service\MyClassConfirmationService": argument "$request" of method "__construct()" references class "Symfony\Component\HttpFoundation\Request" but no such service exists.
您不应将 Request
注入您的服务。您应该使用 Symfony\Component\HttpFoundation\RequestStack
而不是 Request
。此外,您应该检查 $requestStack->getCurrentRequest()
是否 return null
。我想你在容器初始化过程中遇到了这样的错误,但你只执行了一个脚本(测试),当然,你没有 Request
。
由于整个事情需要在这里进行更多调整,因此我 post Nikita 的回答引导我的解决方案:
正如他所建议的那样,我用 RequestStack 替换了“我的服务中的请求,效果很好:
/**
* @param LoggerInterface $logger
* @param SerializerInterface $serializer
* @param ExternalClient $externalClient
* @param RequestStack $requestStack
*/
public function __construct(
LoggerInterface $logger,
SerializerInterface $serializer,
ExternalClient $externalClient,
RequestStack $requestStack
) {
$this->logger = $logger;
$this->serializer = $serializer;
$this->externalClient = $externalClient;
$this->request = $requestStack->getCurrentRequest();
$this->params = $this->request->query;
}
在我的测试中,我伪造了这样的请求:
$this->request = new Request($getParams, [], [], [], [], [], null);
$this->requestStack = new RequestStack();
$this->requestStack->push($this->request);
然而,随着我的下一个问题的解决,我的 class 也需要记录器和序列化器。
对于 Logger,我使用了我专门为此测试情况创建的通用 Loggerclass。但这让我得到序列化器,我想要真正的序列化器,否则我可能会坚持使用几乎无用的单元测试。
我就是这么做的:
public function setUp()
{
$kernel = self::bootKernel();
$container = self::$container;
$this->serializer = $container->get('jms_serializer.serializer');
}
然后这给了我来自容器的真正的序列化器。
现在我可以让模拟的外部客户端给我模拟的答案,并且我可以测试我的服务的反应,而无需打扰外部服务。
我正在测试一项服务,该服务主要是序列化对象并通过服务将其发送到外部系统。
如果我创建典型的单元测试,我会模拟序列化程序和服务的响应,它与外部系统联系。事实上,除了在我的对象中调用一堆 setter 方法之外,没有太多要测试的了。
备选方案是使用 KernelTestCase 并创建功能测试,这很好,除非我不想联系外部系统,而是仅为此 "external" 服务使用模拟。
有没有可能在 Symfony 4 中实现这个? 或者还有其他方法吗?
我现在做的是:
<?php
namespace App\Tests\Service;
use App\Service\MyClassService;
use App\Service\ExternalClient\ExternalClient;
use JMS\Serializer\Serializer;
use JMS\Serializer\SerializerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;
class MyClassServiceTest extends KernelTestCase
{
/** @var LoggerInterface */
private $logger;
/** @var Serializer */
private $serializer;
/** @var ExternalClient */
private $externalClient;
/** @var RequestInterface */
private $request;
/** @var MyClassService */
private $myClassService;
public function setUp()
{
$kernel = self::bootKernel();
$this->logger = $kernel->getContainer()->get(LoggerInterface::class);
$this->serializer = $kernel->getContainer()->get(SerializerInterface::class);
$this->externalClient = $this->createMock(ExternalClient::class);
}
public function testPassRegistrationData()
{
$getParams = [
'amount' => '21.56',
'product_id' => 867,
'order_id' => '47t34g',
'order_item_id' => 2,
'email' => 'kiki%40bubu.com',
];
$this->generateMyClassService($getParams);
$userInformation = $this->myClassService->passRegistrationData();
var_dump($userInformation);
}
/**
* generateMyClassService
*
* @param $getParams
*
* @return MyClass
*/
private function generateMyClassService($getParams)
{
$this->request = new Request($getParams, [], [], [], [], [], null);
$this->myClassService = new MyClassService(
$this->logger,
$this->serializer,
$this->externalClient,
$this->request
);
}
}
返回此错误:
Symfony\Component\DependencyInjection\Exception\RuntimeException: Cannot autowire service "App\Service\MyClassConfirmationService": argument "$request" of method "__construct()" references class "Symfony\Component\HttpFoundation\Request" but no such service exists.
您不应将 Request
注入您的服务。您应该使用 Symfony\Component\HttpFoundation\RequestStack
而不是 Request
。此外,您应该检查 $requestStack->getCurrentRequest()
是否 return null
。我想你在容器初始化过程中遇到了这样的错误,但你只执行了一个脚本(测试),当然,你没有 Request
。
由于整个事情需要在这里进行更多调整,因此我 post Nikita 的回答引导我的解决方案:
正如他所建议的那样,我用 RequestStack 替换了“我的服务中的请求,效果很好:
/**
* @param LoggerInterface $logger
* @param SerializerInterface $serializer
* @param ExternalClient $externalClient
* @param RequestStack $requestStack
*/
public function __construct(
LoggerInterface $logger,
SerializerInterface $serializer,
ExternalClient $externalClient,
RequestStack $requestStack
) {
$this->logger = $logger;
$this->serializer = $serializer;
$this->externalClient = $externalClient;
$this->request = $requestStack->getCurrentRequest();
$this->params = $this->request->query;
}
在我的测试中,我伪造了这样的请求:
$this->request = new Request($getParams, [], [], [], [], [], null);
$this->requestStack = new RequestStack();
$this->requestStack->push($this->request);
然而,随着我的下一个问题的解决,我的 class 也需要记录器和序列化器。 对于 Logger,我使用了我专门为此测试情况创建的通用 Loggerclass。但这让我得到序列化器,我想要真正的序列化器,否则我可能会坚持使用几乎无用的单元测试。
我就是这么做的:
public function setUp()
{
$kernel = self::bootKernel();
$container = self::$container;
$this->serializer = $container->get('jms_serializer.serializer');
}
然后这给了我来自容器的真正的序列化器。 现在我可以让模拟的外部客户端给我模拟的答案,并且我可以测试我的服务的反应,而无需打扰外部服务。