如何用PHPUnit测试symfony控制台命令

103 阅读1分钟

在这个例子中,我们要用PHPUnit来测试一个控制台命令。

Composer.json

这就是我们的设置的样子:

{
    ...
    "require": {
        "php": ">=7.1.1",
        ...
    },
    "require-dev": {
        "phpunit/phpunit": "^6.4"
    },
    "config": {
        "platform": {
            "php": "7.1.11"
        },
        ...
    },
    ...
}

CustomerCommand

你可以通过运行$ bin/console customer --id=1 命令来测试它。

namespace PhpunitBundle\Command;

use PhpunitBundle\Entity\Customer;
use PhpunitBundle\Repository\CustomerRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class CustomerCommand extends Command
{
    private $customerRepository;

    public function __construct(
        CustomerRepository $customerRepository
    ) {
        parent::__construct();

        $this->customerRepository = $customerRepository;
    }

    protected function configure()
    {
        $this
            ->setName('customer')
            ->addOption('id', null, InputOption::VALUE_REQUIRED);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $customer = $this->customerRepository->findOneById($input->getOption('id'));
        if (!$customer instanceof Customer) {
            throw new BadRequestHttpException(
                sprintf('Customer with id [%s] not found', $input->getOption('id'))
            );
        }

        $output->writeln($customer->getName());
    }
}
services:
    phpunit.command.customer:
        class: PhpunitBundle\Command\CustomerCommand
        tags:
            - { name: console.command }
        arguments:
            - "@phpunit.repository.customer"

CustomerCommandTest

namespace tests\PhpunitBundle\Command;

use DateTime;
use PhpunitBundle\Command\CustomerCommand;
use PhpunitBundle\Entity\Customer;
use PhpunitBundle\Repository\CustomerRepository;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class CustomerCommandTest extends TestCase
{
    /** @var CustomerRepository|PHPUnit_Framework_MockObject_MockObject */
    private $customerRepositoryMock;
    /** @var CommandTester */
    private $commandTester;

    protected function setUp()
    {
        $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepository::class)
            ->disableOriginalConstructor()
            ->getMock();

        $application = new Application();
        $application->add(new CustomerCommand($this->customerRepositoryMock));
        $command = $application->find('customer');
        $this->commandTester = new CommandTester($command);
    }

    protected function tearDown()
    {
        $this->customerRepositoryMock = null;
        $this->commandTester = null;
    }

    public function testExecute()
    {
        $id = 1;
        $customer = new Customer();
        $customer->setId($id);
        $customer->setName('Name 1');
        $customer->setDob(new DateTime());

        $this->customerRepositoryMock
            ->expects($this->once())
            ->method('findOneById')
            ->with($id)
            ->willReturn($customer);

        $this->commandTester->execute(['--id' => $id]);

        $this->assertEquals('Name 1', trim($this->commandTester->getDisplay()));
    }

    public function testExecuteShouldThrowExceptionForInvalidCustomerId()
    {
        $id = 666;

        $this->customerRepositoryMock
            ->expects($this->once())
            ->method('findOneById')
            ->with($id)
            ->willReturn(null);

        $this->expectException(BadRequestHttpException::class);
        $this->expectExceptionMessage(sprintf('Customer with id [%s] not found', $id));

        $this->commandTester->execute(['--id' => $id]);
    }
}