Symfony Unit测试自定义事件监听器

时间:2017-11-07 09:07:30

标签: php symfony unit-testing phpunit symfony-3.3

我有自己的事件监听器:

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 inn命令来构造它。所以:

  • 是否可以模拟HttpKernelInterface?它会破坏班级的使用吗?
  • 是否有更好的方法对MyExceptionListener进行单元测试?

1 个答案:

答案 0 :(得分:2)

您测试的方法以GetResponseForExceptionEvent为参数。这是应该被嘲笑的类。

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

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