Получение метода клона, вызываемого для необъектного объекта, при указании команды Symfony, которая использует SymfonyStyle для стилизованного вывода

Я пытаюсь специфицировать команду Symfony и хочу получить форматированный вывод с помощью SymfonyStyle

<?php

namespace Acme\AppBundle\Command;

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

class SomeCommand extends ContainerAwareCommand
{
    //....
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);

        $io->title('Feed import initiated');
    }
}

и файл спецификации:

<?php

namespace spec\Acme\AppBundle\Command;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class SomeCommandSpec extends ObjectBehavior
{
    //...
    function it_fetches_social_feeds(
        ContainerInterface $container,
        InputInterface $input,
        OutputInterface $output,
        SymfonyStyle $symfonyStyle
    ) {
        // With options
        $input->bind(Argument::any())->shouldBeCalled();
        $input->hasArgument('command')->shouldBeCalled();
        $input->isInteractive()->shouldBeCalled();
        $input->validate()->shouldBeCalled();

        $symfonyStyle->title(Argument::any())->shouldBeCalled();

        $this->setContainer($container);
        $this->run($input, $output);
    }
}

но я получаю эту ошибку:

exception [err:Error("__clone method called on non-object")] has been thrown.
 0 vendor/symfony/symfony/src/Symfony/Component/Console/Style/SymfonyStyle.php:50
   throw new PhpSpec\Exception\ErrorException("__clone method called on ...")
 1 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:866
   Symfony\Component\Console\Command\Command->run([obj:Symfony\Component\Console\Input\ArgvInput], [obj:Symfony\Component\Console\Output\ConsoleOutput])
 2 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:193
   Symfony\Component\Console\Application->doRunCommand([obj:PhpSpec\Console\Command\RunCommand], [obj:Symfony\Component\Console\Input\ArgvInput], [obj:Symfony\Component\Console\Output\ConsoleOutput])
 3 vendor/phpspec/phpspec/src/PhpSpec/Console/Application.php:102
   Symfony\Component\Console\Application->doRun([obj:Symfony\Component\Console\Input\ArgvInput], [obj:Symfony\Component\Console\Output\ConsoleOutput])
 4 vendor/phpspec/phpspec/bin/phpspec:26
   Symfony\Component\Console\Application->run()
 5 vendor/phpspec/phpspec/bin/phpspec:28
   {closure}("3.2.2")

в строке 50 SymfonyStyle находится:

public function __construct(InputInterface $input, OutputInterface $output)
{
    $this->input = $input;
    /* line 50 */ $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
    // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
    $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);

    parent::__construct($output);
}

и phpspec жалуется на

clone $output->getFormatter()

Я делаю что-то не так или я что-то упускаю?

Обновить

это мое let метод:

function let(SymfonyStyle $symfonyStyle, InputInterface $input, OutputInterface $output)
{
    $symfonyStyle->beConstructedWith([$input->getWrappedObject(), $output->getWrappedObject()]);
}

2 ответа

Решение

Вы скучаете по этому

function it_fetches_social_feeds(
    ContainerInterface $container,
    InputInterface $input,
    OutputInterface $output,
    SymfonyStyle $symfonyStyle
) {
    // .... other code  
    $prophet = new Prophet();
    $formatter = $prophet->prophesize(OutputFormatterInterface::class);
    $output->getFormatter()->willReturn($formatter);
    // .... more code
}

Вы можете разместить его там, где хотите, но до завершения вызова.

Таким образом, вы создали Stub это в основном Double с поведением и без ожиданий. Вы можете видеть его как "прокси", который будет перехватывать вызовы методов и возвращать то, что вы "учите" возвращать.
В вашем примере вещи разбиваются как ваш двойник OutputInterface вернется null поскольку это не "настоящий" объект.
Я также предлагаю заглушить getVerbosity поведение, если вам нужно сделать различные виды спецификаций.

Кстати, вы можете прочитать больше о двойниках в руководстве phpspec и руководстве по пророчествам

Рассматривая пример, SymfonyStyle никогда не передается в классе spec.
Функция execute команды также не передает ее, только классы ввода и вывода (класс стиля, который она создала как локальная переменная).

Другие вопросы по тегам