Xero
Xero

Reputation: 4175

phpunit must be traversable or implement interface Iterator

I'm trying to unit test my service, containing a dependency Finder Component of symfony2(http://symfony.com/doc/current/components/finder.html).

I'm getting :

[Exception] Objects returned by Mock_Finder_91776c5c::getIterator() must be traversable or implement interface Iterator

The service :

public function getFile($fileName, $path = '/')
    {

        if($this->wrapper == null || $this->connection == null)
            throw new LogicException("call method setFTP first");

        // get file on ftp server
        $this->connection->open();
        $downloadComplete = $this->wrapper->get($this->tmpDir . $fileName, $path . $fileName);
        $this->connection->close();

        if($downloadComplete == false)
            return false; // TODO exception ?

        // return file downloaded
        $this->finder->files()->in(__DIR__);
        foreach ($this->finder as $file) {
            return $file;
        }

        return false; // TODO exception ?

    }

And the test

class FtpServiceTest extends \PHPUnit_Framework_TestCase
{

    protected $connectionMock;
    protected $ftpWrapperMock;
    protected $finderMock;

    protected function setUp()
    {
        $this->connectionMock = $this->getConnectionMock();
        $this->ftpWrapperMock = $this->getFTPWrapperMock();
        $this->finderMock = $this->getFinderMock();
    }

    protected function tearDown()
    {
    }

    private function getFinderMock()
    {
        return $this->getMockBuilder(Finder::class)
            ->disableOriginalConstructor()
            ->getMock('Iterator');
    }

    private function getConnectionMock()
    {
        return $this->getMockBuilder(Connection::class)
            ->disableOriginalConstructor()
            ->getMock();
    }

    private function getFTPWrapperMock()
    {
        return $this->getMockBuilder(FTPWrapper::class)
            ->disableOriginalConstructor()
            ->getMock();
    }

    // tests
    public function testMe()
    {

        // arrange
        $host = 'localhost';
        $user = 'user';
        $password = '1234';

        $filesArray = new ArrayObject(array(''));

        $service = new FtpService('var/tmp/');
        $service->setFTP($this->connectionMock, $this->ftpWrapperMock);
        $service->setFinder($this->finderMock);

        $this->connectionMock
            ->expects($this->once())
            ->method('open');

        $this->ftpWrapperMock
            ->expects($this->once())
            ->method('get')
            ->will($this->returnValue(true));

        $this->connectionMock
            ->expects($this->once())
            ->method('close');

        $this->finderMock
            ->expects($this->once())
            ->method('files')
            ->will($this->returnValue($this->finderMock));

        $this->finderMock
            ->expects($this->once())
            ->method('in')
            ->will($this->returnValue($filesArray));

        // act
        $file = $service->getFile('/file.zip');

        // assert
        $this->assertInstanceOf(SplFileInfo::class, $file);

    }

}

Upvotes: 2

Views: 4732

Answers (1)

Matteo
Matteo

Reputation: 39380

The mocked instance of the Finder class needs to implement/mock the method getIterator. The getMock method does not accept arguments so don't pass the string 'Iterator'—change the code as follows:

private function getFinderMock()
{
    return $this->getMockBuilder(Finder::class)
        ->disableOriginalConstructor()
        ->getMock();
}

And add the Mocked expectation in the test method, for example:

    $this->finderMock->expects($this->once())
        ->method('getIterator')
        ->willReturn(new \ArrayObject([$this->createMock(SplFileInfo::class)]));

Hope this helps.

Upvotes: 7

Related Questions