Symfony 单元测试自定义事件监听器

Symfony Unit test Custom Event Listener

我有自己的事件监听器:

namespace AppBundle\EventHandlers;

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;

class MyExceptionListener
{
    /**
    * @var string
    */
    private $env;

    /**
     * @var null|\Twig_Environment
     */
    private $twig=null;

    private $forOfourExceptions=[
      AppBunble\Exceptions\ApiReturnedNoDataException::class,
      AppBunble\Exceptions\DatabaseReturnedNoDataException::class,
      Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class
    ];

    public function __construct($env,\Twig_Environment $twig)
    {
      $this->env = $env;
      $this->twig=$twig;
    }

    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // You get the exception object from the received event
        $exception = $event->getException();

        $exceptionClass=get_class($exception);

        // Customize your response object to display the exception details
        $response = new Response();

        $content="";
        if(in_array($exceptionClass,ExceptionMapping::RETURN_ERROR_FOUR_HUNDRED_FOUR)){
            $response ->setStatusCode(Response::HTTP_NOT_FOUND);
            $content = $this->twig->render('error_404.html.twig');
        } else {
            $response ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);

            if($this->env == 'dev'){//Only on production set response
                return;
            }
        }

        $response->setContent($content);

        // Send the modified response object to the event
        $event->setResponse($response);
    }
}

我做了以下单元测试:

namespace AppBundle\Tests\EventHandlers;

use AppBundle\Exceptions\ApiReturnedNoDataException;
use PHPUnit\Framework\TestCase;
use AppBundle\EventHandlers\MyExceptionListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class ExceptionListenerTest extends TestCase
{

    public function testnoDataExceptionTest()
    {
        /**
         * @var ExceptionListener
         */
        $class=$this->constructClass();

        $exception=new ApiReturnedNoDataException('giberish');

        $responseEvent=$this->getMockBuilder(GetResponseForExceptionEvent::class)
                                ->disableOriginalConstructor()
                                ->getMock();

        $responseEvent->method('getException')->willReturn( $exception );

        /**
         * @var Response
         */
        $class->onKernelException($responseEvent);

        $this->assertEquals($response->getStatusCode(),Response::HTTP_NOT_FOUND);
    }

    /**
     * @return ExceptionListener
     */
    private function constructClass()
    {

        $twigMock=$this->getMockBuilder(\Twig_Environment::class)
                            ->disableOriginalConstructor()
                            ->getMock();

        $twigMock->method('render')->willReturn('');

        $exceptionListener= new ExceptionListener('prod',$twigMock);

        return $exceptionListener;
    }
}

但 phpoUnit 正确抛出:

1) AppBundle\Tests\Managers\ExceptionListenerTest::testnoDataExceptionTest
Error: Call to a member function getStatusCode() on null

所以我需要构造一个正确的GetResponseForExceptionEvent但是它的构造函数需要传递一个HttpKernelInterface客栈命令来构造它。所以:

您测试的方法将 GetResponseForExceptionEvent 作为参数。这是应该嘲笑的class。

如果您正在测试 GetResponseForExceptionEvent class(已由 Symfony 开发人员测试),HttpKernelInterface 的模拟是可以接受的。

单元测试不会测试另一个 class 内部发生的事情,只是您测试的 class 完成它的工作并调用其他 class 所需的方法。这意味着测试 MyExceptionListener 实际上是在断言 GetResponseForExceptionEvent 的方法 setResponse 是用正确的参数调用的。