【问题标题】:Symfony 4 Mock service in functional test功能测试中的 Symfony 4 Mock 服务
【发布时间】:2020-05-15 17:11:12
【问题描述】:

我正在测试一项服务,该服务主要是序列化对象并通过服务将其发送到外部系统。

如果我创建典型的单元测试,我会模拟序列化程序和服务的响应,这些响应与外部系统联系。事实上,除了在我的对象中调用一堆 setter 方法之外,剩下的测试就不多了。

替代方法是使用 KernelTestCase 并创建功能测试,这很好,除非我不想联系外部系统,而是只为这个“外部”服务使用模拟。

有没有可能在 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.

【问题讨论】:

    标签: unit-testing symfony4


    【解决方案1】:

    您不应将Request 注入您的服务中。您应该使用Symfony\Component\HttpFoundation\RequestStack 而不是Request。此外,您应该检查$requestStack-&gt;getCurrentRequest() 是否不返回null。我想你在容器初始化的过程中遇到了这样的错误,但你只执行了一个脚本(测试),当然,你没有Request

    【讨论】:

      【解决方案2】:

      由于整个事情需要在这里进行更多调整,我发布了我的解决方案,尼基塔的回答引导我:

      正如他建议的那样,我用 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);
      

      然而,解决了我的下一个问题,因为我的班级还要求提供记录器和序列化程序。 对于 Logger,我使用了专门为这种测试情况创建的通用 Loggerclass。但这让我得到了序列化程序,我想要真正的序列化程序,否则我可能会坚持使用一个几乎没用的单元测试。

      这就是我所做的:

      public function setUp()
      {
          $kernel = self::bootKernel(); 
          $container = self::$container;
          $this->serializer = $container->get('jms_serializer.serializer');
      }
      

      然后这给了我容器中真正的序列化程序。 现在我可以让我模拟的外部客户端给我模拟的答案,我可以测试我的服务的反应,而无需打扰外部服务。

      【讨论】:

      • 顺便说一句关于记录器。我总是使用独白,在我的测试中我这样模拟它:$this-&gt;createMock(Logger::class)
      • 如果写入了特定级别的日志消息,则无需检查测试即可。
      猜你喜欢
      • 1970-01-01
      • 2020-08-19
      • 1970-01-01
      • 2013-11-12
      • 2022-01-06
      • 2021-02-06
      • 1970-01-01
      • 2015-10-29
      • 1970-01-01
      相关资源
      最近更新 更多