Dimitrios Desyllas
Dimitrios Desyllas

Reputation: 10162

Symfony Unit test Custom Event Listener

I have mage my own 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);
    }
}

And I did the following Unit Test:

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;
    }
}

But The phpoUnit correctly throws:

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

So I need to construct a correct GetResponseForExceptionEvent but its constructor needs to pass an HttpKernelInterface inn order to construct it. So:

Upvotes: 2

Views: 3250

Answers (1)

svgrafov
svgrafov

Reputation: 2024

Your tested method takes GetResponseForExceptionEvent as argument. This is the class that should be mocked.

Mocking of HttpKernelInterface would be acceptable if you were testing GetResponseForExceptionEvent class(which is already tested by Symfony developers).

Unit test does not test what happens inside another class, only that your tested class does its job and calls needed methods of other class. This means that testing MyExceptionListener is practically asserting that method setResponse of GetResponseForExceptionEvent is called with correct argument.

Upvotes: 2

Related Questions