I'm trying to spec Symfony command and I want to have formated output with 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');
}
}
and spec file:
<?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);
}
}
but I'm getting this error:
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")
on line 50 of SymfonyStyle is:
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);
}
and phpspec is complainig about
clone $output->getFormatter()
Am I doing something wrong, or am I missing something?
Update
this is my let
method:
function let(SymfonyStyle $symfonyStyle, InputInterface $input, OutputInterface $output)
{
$symfonyStyle->beConstructedWith([$input->getWrappedObject(), $output->getWrappedObject()]);
}
You are missing this
You can place it where you want but before the call is done.
In that way you have created a
Stub
that is basically aDouble
with behavior and without expectations. You can see it as a "proxy" that will intercept method calls and returns what you "teach" it to return.In your example things get broken as your double
OutputInterface
would returnnull
as it's not a "real" object.I also suggest to stub the
getVerbosity
behavior if you need to do different kind of specs.BTW you can read more about doubles in phpspec guide and prophecy guide