It can be tricky when testing event listeners that deal with user requests where Request and ParameterBag classes with phpspec. In this example, user will send a request to the controller and we will intercept it to throw an exception instead of returning a response. As simple as that!


OrderController


namespace Application\FrontendBundle\Controller;

use Application\FrontendBundle\Service\OrderService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("order", service="application_frontend.controller.order")
*/
class OrderController
{
private $orderService;

public function __construct(OrderService $orderService)
{
$this->orderService = $orderService;
}

/**
* @param Request $request
*
* @Method({"POST"})
* @Route("/create/{car}", defaults={"car"="bmw"}, name="create_car_order")
*
* @return Response
*/
public function createAction(Request $request, $car)
{
return new Response(
'ORDERING:'.$car.PHP_EOL.
'PAYLOAD:'.$request->getContent().PHP_EOL.
'QUERY:'.$request->getQueryString()
);
}
}

Test without listener


Send request below to POST /order/create/mercedes?parent=mclaren address.


{
"customer_name": "inanzzz",
"car_make": "mercedes",
"car_model": "sl500"
}

Result


Status 200 OK

ORDERING:mercedes
PAYLOAD:{
"customer_name": "inanzzz",
"car_make": "mercedes",
"car_model": "sl500"
}
QUERY:parent=mclaren

OrderCreateListener


namespace Application\FrontendBundle\EventListener;

use Application\FrontendBundle\Controller\OrderController;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class OrderCreateListener
{
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
return;
}

if (
$event->isMasterRequest() &&
$this->isOrderController($controller) &&
$this->isCreateCarMethod($event)
) {
$params = $event->getRequest()->attributes->get('_route_params');
$query = $event->getRequest()->getQueryString();

throw new BadRequestHttpException(
sprintf('PARAMS:%s - QUERY:%s', json_encode($params), $query)
);
}

return;
}

private function isOrderController(array $controller)
{
return $controller[0] instanceof OrderController ? true : false;
}

private function isCreateCarMethod(FilterControllerEvent $event)
{
return $event->getRequest()->attributes->get('_route') == 'create_car_order' ? true : false;
}

}

Listeners.yml


services:
application_frontend.event_listener.order_create:
class: Application\FrontendBundle\EventListener\OrderCreateListener
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

Test with listener


Send request below to POST /order/create/mercedes?parent=mclaren address.


{
"customer_name": "inanzzz",
"car_make": "mercedes",
"car_model": "sl500"
}

Result


Status 400 Bad Request

PARAMS:{"car":"mercedes"} - QUERY:parent=mclaren
400 Bad Request - BadRequestHttpException

PhpSpec test


Critical section here is the let() function. Although there isn't any _construct() method in our controller and listener, in this occasion it is compulsory for our last test case it_should_throw_an_exception_if_a_user_calls_create_car_order_method_of_order_controller to pass.


namespace spec\Application\FrontendBundle\EventListener;

use Application\FrontendBundle\Controller\OrderController;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class OrderCreateListenerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Application\FrontendBundle\EventListener\OrderCreateListener');
}

function let(Request $request, ParameterBag $attributes)
{
$request->attributes = $attributes;

$this->beConstructedWith();
}

function it_should_return_nothing_if_the_event_controller_is_not_an_array(
FilterControllerEvent $event
) {
$controller = null;
$event->getController()->willReturn(null);

$this->onKernelController($event)->shouldReturn(null);
}

function it_should_return_nothing_if_the_event_request_is_not_master_request(
FilterControllerEvent $event
) {
$controller = [];
$event->getController()->willReturn($controller);

$isMasterRequest = false;
$event->isMasterRequest()->willReturn($isMasterRequest);

$this->onKernelController($event)->shouldReturn(null);
}

function it_should_return_nothing_if_the_event_is_not_fired_for_order_controller(
FilterControllerEvent $event
) {
$controller = ['SomeController'];
$event->getController()->willReturn($controller);

$isMasterRequest = true;
$event->isMasterRequest()->willReturn($isMasterRequest);

$this->onKernelController($event)->shouldReturn(null);
}

function it_should_return_nothing_if_the_order_controller_event_is_not_fired_for_create_car_order_method_route(
FilterControllerEvent $event,
OrderController $orderController,
Request $request,
ParameterBag $attributes
) {
$controller = [$orderController];
$event->getController()->willReturn($controller);

$isMasterRequest = true;
$event->isMasterRequest()->willReturn($isMasterRequest);

$event->getRequest()->willReturn($request);

$route = 'some_route';
$attributes->get('_route')->shouldBeCalled()->willReturn($route);

$this->onKernelController($event)->shouldReturn(null);
}

function it_should_throw_an_exception_if_a_user_calls_create_car_order_method_of_order_controller(
FilterControllerEvent $event,
OrderController $orderController,
Request $request,
ParameterBag $attributes
) {
$controller = [$orderController];
$event->getController()->willReturn($controller);

$isMasterRequest = true;
$event->isMasterRequest()->willReturn($isMasterRequest);

$event->getRequest()->willReturn($request);

$route = 'create_car_order';
$attributes->get('_route')->shouldBeCalled()->willReturn($route);

$params = ['car' => 'mercedes'];
$attributes->get('_route_params')->shouldBeCalled()->willReturn($params);

$query = 'parent=mclaren';
$request->getQueryString()->willReturn($query);

$this->shouldThrow(
new BadRequestHttpException(
sprintf('PARAMS:%s - QUERY:%s', json_encode($params), $query)
)
)->during(
'onKernelController',
[$event]
);
}
}

Result


inanzzz$ bin/phpspec run spec/Application/FrontendBundle/EventListener/OrderCreateListenerSpec.php -v
100% 6
1 specs
6 examples (6 passed)
50ms