0

I'm pretty new to Symfony and phpspec so please feel free to criticize heavily. So the issue is that I'm constantly getting PHP Fatal error: Call to a member function write() on a non-object.

Basically the class which is tested should write output to the console. In the constructor first I'm creating stream and then passing this stream to another class which is responsible for the output in the console. This is the main code.

Class ScreenshotTaker :

<?php

namespace Bex\Behat\ScreenshotExtension\Service;

use Behat\Mink\Mink;
use Behat\Testwork\Output\Printer\StreamOutputPrinter;
use Bex\Behat\ScreenshotExtension\Driver\ImageDriverInterface;
use Behat\Testwork\Output\Printer\Factory\ConsoleOutputFactory;
/**
 * This class is responsible for taking screenshot by using the Mink     session
 *
 * @license http://opensource.org/licenses/MIT The MIT License
 */
class ScreenshotTaker
{
    /** @var Mink $mink */
    private $mink;

    /** @var ConsoleOutputFactory $output */
    private $output;

    /** @var ImageDriverInterface[] $imageDrivers */
    private $imageDrivers;

    /** @var StreamOutputPrinter $outputStream */
    private $outputStream;

    /**
     * Constructor
     *
     * @param Mink $mink
     * @param ConsoleOutputFactory $output
     * @param ImageDriverInterface[] $imageDrivers
     */
    public function __construct(Mink $mink, ConsoleOutputFactory $output, array $imageDrivers)
    {
        $this->mink = $mink;
        $this->output = $output;
        $this->imageDrivers = $imageDrivers;
        $this->outputStream = new StreamOutputPrinter ($output);
    }

    /**
     * Save the screenshot as the given filename
     *
     * @param string $fileName
     */
    public function takeScreenshot($fileName = 'failure.png')
    {
        try {
            $screenshot = $this->mink->getSession()->getScreenshot();

            foreach ($this->imageDrivers as $imageDriver) {
                $imageUrl = $imageDriver->upload($screenshot, $fileName);
                $this->outputStream->writeln('Screenshot has been taken. Open image at ' . $imageUrl);
            }
        } catch (\Exception $e) {
            $this->outputStream->writeln($e->getMessage());
        }
    }
}

Now this is the phpspec test.I'm passing the ConsoleOutputFactory which is used in the constructor but I'm getting

PHP Fatal error: Call to a member function write() on a non-object in Behat/Testwork/Output/Printer/StreamOutputPrinter.php on line 125

This write method is part of StreamOutputPrinter. Can you tell me what Im missing here?

ScreenshotTakerSpec:

<?php

namespace spec\Bex\Behat\ScreenshotExtension\Service;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Behat\Testwork\Output\Printer\Factory\ConsoleOutputFactory;
use Bex\Behat\ScreenshotExtension\Driver\Local;
use Behat\Testwork\Output\Printer\StreamOutputPrinter;

/**
 * Unit test of the class ScreenshotTaker
 *
 * @license http://opensource.org/licenses/MIT The MIT License
 */
class ScreenshotTakerSpec extends ObjectBehavior
{
    function let(Mink $mink, ConsoleOutputFactory $output, Local $localImageDriver)
    {
        $this->beConstructedWith($mink, $output, [$localImageDriver]);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Bex\Behat\ScreenshotExtension\Service\ScreenshotTaker');
    }

    function it_should_call_the_image_upload_with_correct_params(Mink $mink, Session $session, Local $localImageDriver)
    {
        $mink->getSession()->willReturn($session);
        $session->getScreenshot()->willReturn('binary-image');
        $localImageDriver->upload('binary-image', 'test.png')->shouldBeCalled();

        $this->takeScreenshot('test.png');
    }
}
alexander.polomodov
  • 5,396
  • 14
  • 39
  • 46
tslid
  • 315
  • 1
  • 4
  • 16

1 Answers1

1

You should mock the call to outputFactory->createOutput() that is in StreamOutputPrinter line 144, but mocking something that is in another class is a smell. So I'd recommend to move the stream logic to a new class, eg StreamOutputPrinterFactory, and inject this factory:

    public function __construct(Mink $mink, StreamOutputPrinterFactory $factory, array $imageDrivers)
{
    $this->mink = $mink;
    $this->imageDrivers = $imageDrivers;
    $this->outputStream = $factory->createNew();
}

Now you can mock any calls to $this->outputStream.

You should also call createNew() when it's needed, not in the constructor. Let me know if you need further help.

gvf
  • 1,039
  • 7
  • 6