在symfony应用程序中用PHPUnit测试事件监听器的实例

166 阅读1分钟

假设我们使用一个事件监听器,在一个文本文件和应用程序日志中记录击中CustomerController::getOneAction($id) 端点/方法的用户请求。在这个例子中,我们将只是测试事件监听器,所以只需注意它。

CustomerController

namespace Inanzzz\ApplicationBundle\Controller;

use Inanzzz\ApplicationBundle\Service\CustomerService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Response;

/**
 * @Route("/customers", service="inanzzz_application.controller.customer")
 */
class CustomerController
{
    private $customerService;

    public function __construct(
        CustomerService $customerService
    ) {
        $this->customerService = $customerService;
    }

    /**
     * @param int $id
     *
     * @Method({"GET"})
     * @Route("/{id}", requirements={"id"="\d+"})
     *
     * @return Response
     */
    public function getOneAction($id)
    {
        $result = $this->customerService->getOne($id);

        return new Response(json_encode($result));
    }
}
services:
    inanzzz_application.controller.customer:
        class: Inanzzz\ApplicationBundle\Controller\CustomerController
        arguments:
            - "@inanzzz_application.service.customer"

CustomerControllerListener

namespace Inanzzz\ApplicationBundle\Listener;

use Inanzzz\ApplicationBundle\Controller\CustomerController;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;

class CustomerControllerListener
{
    private $logger;
    private $logFilePath;

    public function __construct(
        LoggerInterface $logger,
        $logFilePath
    ) {
        $this->logger = $logger;
        $this->logFilePath = $logFilePath;
    }

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

        if (!isset($event->getController()[0])) {
            return;
        }

        if (!$event->getController()[0] instanceof CustomerController) {
            return;
        }

        if (!$event->isMasterRequest()) {
            return;
        }

        $request = $event->getRequest();

        $id = $request->get('id');
        if (!$id) {
            return;
        }

        $this->log($id);
    }

    private function log($id)
    {
        $message = sprintf('Customer [%s] is called.', $id);

        $this->logger->info($message);
        file_put_contents($this->logFilePath, $message.PHP_EOL, FILE_APPEND);
    }
}
services:
    inanzzz_application.listener.customer_controller:
        class: Inanzzz\ApplicationBundle\Listener\CustomerControllerListener
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
        arguments:
            - "@logger"
            - "%kernel.root_dir%/../var/logs/customer.log"

日志文件

正如你在上面看到的,日志被保存在live 环境下的/var/logs/customer.log 文件中,但我们将在我们的test 环境下模拟它。创建一个名为/tests/Inanzzz/ApplicationBundle/Mock/log/customer.log 的文件,这样我们的测试就可以将日志保存在里面。

CustomerControllerListenerTest

namespace tests\Inanzzz\ApplicationBundle\Command;

use Inanzzz\ApplicationBundle\Controller\CustomerController;
use Inanzzz\ApplicationBundle\Listener\CustomerControllerListener;
use Inanzzz\ApplicationBundle\Service\CustomerService;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use stdClass;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class CustomerControllerListenerTest extends TestCase
{
    const LOG_PATTERN = 'Customer [%s] is called.';
    const LOG_FILE_PATH = __DIR__.'/../Mock/log/customer.log';

    /** @var CustomerControllerListener */
    private $customerControllerListener;

    protected function setUp()
    {
        /** @var LoggerInterface $loggerInterfaceMock */
        $loggerInterfaceMock = $this->getMockBuilder(LoggerInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->customerControllerListener = new CustomerControllerListener(
            $loggerInterfaceMock,
            self::LOG_FILE_PATH
        );
    }

    protected function tearDown()
    {
        $this->customerControllerListener = null;
        file_put_contents(self::LOG_FILE_PATH, null);
    }

    public function testOnKernelControllerWillReturnNullIfEventControllerIsNull()
    {
        /** @var FilterControllerEvent|\PHPUnit_Framework_MockObject_MockObject $filterControllerEventMock */
        $filterControllerEventMock = $this->getMockBuilder(FilterControllerEvent::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEventMock
            ->expects($this->once())
            ->method('getController')
            ->willReturn(null);

        $result = $this->customerControllerListener->onKernelController($filterControllerEventMock);

        $this->assertNull($result);
    }

    public function testOnKernelControllerWillReturnNullIfEventControllerIsEmptyArray()
    {
        /** @var FilterControllerEvent|\PHPUnit_Framework_MockObject_MockObject $filterControllerEventMock */
        $filterControllerEventMock = $this->getMockBuilder(FilterControllerEvent::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEventMock
            ->expects($this->exactly(2))
            ->method('getController')
            ->willReturn([]);

        $result = $this->customerControllerListener->onKernelController($filterControllerEventMock);

        $this->assertNull($result);
    }

    public function testOnKernelControllerWillReturnNullIfEventControllerIsInvalidObject()
    {
        /** @var FilterControllerEvent|\PHPUnit_Framework_MockObject_MockObject $filterControllerEventMock */
        $filterControllerEventMock = $this->getMockBuilder(FilterControllerEvent::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEventMock
            ->expects($this->exactly(3))
            ->method('getController')
            ->willReturn([new stdClass()]);

        $result = $this->customerControllerListener->onKernelController($filterControllerEventMock);

        $this->assertNull($result);
    }

    public function testOnKernelControllerWillReturnNullIfRequestIsNotMaster()
    {
        /** @var HttpKernelInterface $httpKernelInterfaceMock */
        $httpKernelInterfaceMock = $this->getMockBuilder(HttpKernelInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        /** @var CustomerService $customerServiceMock */
        $customerServiceMock = $this->getMockBuilder(CustomerService::class)
            ->disableOriginalConstructor()
            ->getMock();
        $customerController = new CustomerController($customerServiceMock);
        $callable = [$customerController, 'getOneAction'];

        /** @var Request $requestMock */
        $requestMock = $this->getMockBuilder(Request::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEvent = new FilterControllerEvent(
            $httpKernelInterfaceMock,
            $callable,
            $requestMock,
            HttpKernelInterface::SUB_REQUEST
        );

        $result = $this->customerControllerListener->onKernelController($filterControllerEvent);

        $this->assertNotEquals(HttpKernelInterface::MASTER_REQUEST, $filterControllerEvent->isMasterRequest());
        $this->assertNull($result);
    }

    public function testOnKernelControllerWillReturnNullIfRequestIdParameterIsMissing()
    {
        /** @var FilterControllerEvent|\PHPUnit_Framework_MockObject_MockObject $filterControllerEventMock */
        $filterControllerEventMock = $this->getMockBuilder(FilterControllerEvent::class)
            ->disableOriginalConstructor()
            ->getMock();

        /** @var CustomerController|\PHPUnit_Framework_MockObject_MockObject $customerControllerMock */
        $customerControllerMock = $this->getMockBuilder(CustomerController::class)
            ->disableOriginalConstructor()
            ->getMock();

        /** @var Request|\PHPUnit_Framework_MockObject_MockObject $requestMock */
        $requestMock = $this->getMockBuilder(Request::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEventMock
            ->expects($this->exactly(3))
            ->method('getController')
            ->willReturn([$customerControllerMock]);

        $filterControllerEventMock
            ->expects($this->once())
            ->method('isMasterRequest')
            ->willReturn(HttpKernelInterface::MASTER_REQUEST);

        $filterControllerEventMock
            ->expects($this->once())
            ->method('getRequest')
            ->willReturn($requestMock);

        $requestMock
            ->expects($this->once())
            ->method('get')
            ->with('id')
            ->willReturn(null);

        $result = $this->customerControllerListener->onKernelController($filterControllerEventMock);

        $this->assertNull($result);
    }

    public function testOnKernelControllerWillLogRequest()
    {
        /** @var FilterControllerEvent|\PHPUnit_Framework_MockObject_MockObject $filterControllerEventMock */
        $filterControllerEventMock = $this->getMockBuilder(FilterControllerEvent::class)
            ->disableOriginalConstructor()
            ->getMock();

        /** @var CustomerController|\PHPUnit_Framework_MockObject_MockObject $customerControllerMock */
        $customerControllerMock = $this->getMockBuilder(CustomerController::class)
            ->disableOriginalConstructor()
            ->getMock();

        /** @var Request|\PHPUnit_Framework_MockObject_MockObject $requestMock */
        $requestMock = $this->getMockBuilder(Request::class)
            ->disableOriginalConstructor()
            ->getMock();

        $filterControllerEventMock
            ->expects($this->exactly(3))
            ->method('getController')
            ->willReturn([$customerControllerMock]);

        $filterControllerEventMock
            ->expects($this->once())
            ->method('isMasterRequest')
            ->willReturn(HttpKernelInterface::MASTER_REQUEST);

        $filterControllerEventMock
            ->expects($this->once())
            ->method('getRequest')
            ->willReturn($requestMock);

        $id = 123;

        $requestMock
            ->expects($this->once())
            ->method('get')
            ->with('id')
            ->willReturn($id);

        $result = $this->customerControllerListener->onKernelController($filterControllerEventMock);

        $this->assertEquals(
            sprintf(self::LOG_PATTERN, $id),
            trim(file_get_contents(self::LOG_FILE_PATH))
        );
        $this->assertNull($result);
    }
}

结果

$ vendor/bin/phpunit
PHPUnit 4.8.36 by Sebastian Bergmann and contributors.

...................

Time: 396 ms, Memory: 8.00MB

OK (19 tests, 44 assertions)