Mock Symfony Command with Mockery

For this test, i will use mockery/mockery a simple PHP mock object framework.

composer require --dev mockery/mockery  

Example Symfony Command

namespace Test\TestBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;  
use Symfony\Component\Console\Input\InputOption;  
use Symfony\Component\Console\Input\InputInterface;  
use Symfony\Component\Console\Output\OutputInterface;

class TestCommand extends ContainerAwareCommand  
{
    protected function configure()
    {
        $this
            ->setName('test:command')
            ->setDescription('Test Command')
            ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'User id');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $userId = $input->getOption('user');

        $userRepository = $this->getContainer()
            ->get('doctrine.orm.entity_manager')
            ->getRepository('TestTestBundle:User');

        $user = $userRepository->findOneById($userId);

        if (!$user) {
            $output->writeln('Ops..');

            return 1;
        }

        $output->writeln(
            sprintf('Username: %s', $user->getName())
        );
    }
}

Example Test

namespace Test\TestBundle\Test\Command;

use Doctrine\ORM\EntityManager;  
use Doctrine\ORM\EntityRepository;  
use Mockery as m;  
use Test\TestBundle\Test\TestCommand;  
use Test\TestBundle\Entity\User;  
use Symfony\Component\Console\Tester\CommandTester;  
use Symfony\Component\DependencyInjection\Container;

class TestCommandTest extends \PHPUnit_Framework_TestCase  
{
    protected function getContainer($repository)
    {
        $container = m::mock(Container::class);

        $entityManager = m::mock(EntityManager::class);
        $entityManager->shouldReceive('getRepository')
            ->andReturn($repository);

        $container->shouldReceive('get')
            ->with('doctrine.orm.entity_manager')
            ->andReturn($entityManager);

        return $container;
    }

    protected function getRepositoryWithoutUser()
    {
        $userRepository = m::mock(EntityRepository::class);
        $userRepository->shouldReceive('findOneById')
            ->with('not_found_user_id')
            ->andReturnNull();

        return $userRepository;
    }

    protected function getRepositoryWithUser($fakeUser)
    {
        $userRepository = m::mock(EntityRepository::class);
        $userRepository->shouldReceive('findOneById')
            ->with('user_id')
            ->andReturn($fakeUser);

        return $userRepository;
    }

    public function testExecuteWithoutUser()
    {
        $command = new TestCommand();
        $command->setContainer(
            $this->getContainer(
                $this->getRepositoryWithoutUser()
            )
        );

        $commandTester = new CommandTester($command);
        $commandTester->execute([
            '--user'  => 'not_found_user_id'
        ]);

        $this->assertEquals(1, $commandTester->getStatusCode());
        $this->assertEquals('Ops..', $commandTester->getDisplay());
    }

    public function testExecuteWitUser()
    {
        $fakeUser = (new User())->setName('Max');

        $command = new TestCommand();
        $command->setContainer(
            $this->getContainer(
                $this->getRepositoryWithUser($fakeUser)
            )
        );

        $commandTester = new CommandTester($command);
        $commandTester->execute([
            '--user'  => 'user_id'
        ]);

        $this->assertEquals(0, $commandTester->getStatusCode());
        $this->assertEquals(
            sprintf('Username: %s', $fakeUser->getName()), $commandTester->getDisplay()
        );
    }
}

Fun with Mockery

A complete example of the command flow, with each mock.

namespace Test\TestBundle\Test\Command;

use Doctrine\ORM\EntityManager;  
use Doctrine\ORM\EntityRepository;  
use Mockery as m;  
use Test\TestBundle\Test\TestCommand;  
use Test\TestBundle\Entity\User;  
use Symfony\Component\Console\Application;  
use Symfony\Component\Console\Output\BufferedOutput;  
use Symfony\Component\Console\Input\InputInterface;  
use Symfony\Component\DependencyInjection\Container;

class TestCommandTest extends \PHPUnit_Framework_TestCase  
{
    protected function getApplicationMock($container)
    {
        $appKernel = m::mock(\AppKernel::class);
        $appKernel->shouldReceive('getContainer')
            ->andReturn($container)
            ->getMock();

        $application = m::mock(Application::class);
        $application->shouldReceive('getKernel')
            ->andReturn($appKernel)
            ->getMock();

        return $application;
    }

    protected function getContainer($repository)
    {
        $container = m::mock(Container::class);

        $entityManager = m::mock(EntityManager::class);
        $entityManager->shouldReceive('getRepository')
            ->andReturn($repository);

        $container->shouldReceive('get')
            ->with('doctrine.orm.entity_manager')
            ->andReturn($entityManager);

        return $container;
    }

    protected function getRepositoryWithoutUser()
    {
        $userRepository = m::mock(EntityRepository::class);
        $userRepository->shouldReceive('findOneById')
            ->with('not_found_user_id')
            ->andReturnNull();

        return $userRepository;
    }

    protected function getRepositoryWithUser($fakeUser)
    {
        $userRepository = m::mock(EntityRepository::class);
        $userRepository->shouldReceive('findOneById')
            ->with('user_id')
            ->andReturn($fakeUser);

        return $userRepository;
    }

    public function testExecuteWithoutUser()
    {
        $command = m::mock(TestCommand::class);

        $command->shouldReceive('getApplication')
            ->andReturn(
                $this->getApplicationMock(
                    $this->getContainer(
                        $this->getRepositoryWithoutUser()
                    )
                )
            );

        $input = m::mock(InputInterface::class);

        $input->shouldReceive('getOption')
            ->with('user')
            ->andReturn('not_found_user_id');

        $bufferedOutput = new BufferedOutput();
        $exitCode = $command->execute($input, $bufferedOutput);

        $this->assertEquals(1, $exitCode);
        $this->assertEquals('Ops..', $bufferedOutput);
    }

    public function testExecuteWithUser()
    {
        $command = m::mock(TestCommand::class);

        $fakeUser = (new User())->setName('Max');

        $command->shouldReceive('getApplication')
            ->andReturn(
                $this->getApplicationMock(
                    $this->getContainer(
                        $this->getRepositoryWithUser($fakeUser)
                    )
                )
            );

        $input = m::mock(InputInterface::class);

        $input->shouldReceive('getOption')
            ->with('user')
            ->andReturn('user_id');

        $bufferedOutput = new BufferedOutput();
        $exitCode = $command->execute($input, $bufferedOutput);

        $this->assertEquals(0, $exitCode);
        $this->assertEquals(
            sprintf('Username: %s', $fakeUser->getName()), $bufferedOutput->fetch()
        );
    }
}