6

I want to mock the creation of a file using the vfsstream

class MyClass{

  public function createFile($dirPath)
  {
    $name = time() . "-RT";
    $file = $dirPath . '/' . $name . '.tmp';

    fopen($file, "w+");
    if (file_exists($file)) {
        return $name . '.tmp';
    } else {
        return '';
    }
  }
}

but when I try to test the file creation :

$filename = $myClass->createFile(vfsStream::url('/var/www/app/web/exported/folder'));

I get an error :

failed to open stream: "org\bovigo\vfs\vfsStreamWrapper::stream_open" call failed fopen(vfs://var/www/app/web/exported/folder)

I have see this question talking about mocking the fileSystem but it has no information about the file creation. Does the vfsstream support the file creation with the fopen function? How can I test the file creation?

Matteo
  • 37,680
  • 11
  • 100
  • 115
Dev DOS
  • 1,018
  • 4
  • 18
  • 45
  • Last time I looked, this is explained in [the documentation of vfs-stream](https://github.com/mikey179/vfsStream/wiki). You need to init it first and then relate to the root, see the answer https://stackoverflow.com/a/44776218/367456 below. – hakre Jun 29 '17 at 07:45

2 Answers2

3

try creating with a setup the vsf stream, as example:

$root = vfsStream::setup('root');
$filename = $myClass->createFile($root->url());

hope this help

As working example:

/**
 * @test
 * @group unit
 */
public function itShouldBeTested()
{
    $myClass = new MyClass();

    $root = vfsStream::setup('root');
    $filename = $myClass->createFile($root->url());
    $this->assertNotNull($filename);
    var_dump($filename);
}

this will produce

(dev) bash-4.4$ phpunit -c app --filter=itShouldBeTested PHPUnit 4.8.26 by Sebastian Bergmann and contributors.

.string(17) "1498555092-RT.tmp"

Time: 13.11 seconds, Memory: 200.00MB

OK (1 test, 1 assertion)

Matteo
  • 37,680
  • 11
  • 100
  • 115
0

The OOP way would be like this:

interface FileClientInterface {
    function open($path);
    function exist($path);
}

class FileClient implements FileClientInterface {
    function open($path)
    {
        fopen($path, "w+");
    }

    function exist($path)
    {
        return file_exists($path);
    }
}

class MyClass
{
    private $fileClient;

    /**
     * MyClass constructor.
     * @param $fileClient
     */
    public function __construct(FileClientInterface $fileClient)
    {
        $this->fileClient = $fileClient;
    }

    public function createFile($dirPath)
    {
        $name = time() . "-RT";
        $file = $dirPath . '/' . $name . '.tmp';

        $this->fileClient->open($file);
        fopen($file, "w+");
        if ($this->fileClient->exist($file)) {
            return $name . '.tmp';
        } else {
            return '';
        }

    }
}

class MockFileClient implements FileClientInterface {
    public $calledOpen = 0;
    public $calledExists = 0;

    public function open($path)
    {
        $this->calledOpen ++;
    }

    public function exist($path)
    {
        $this->calledExists++;
    }

}


//not the test
$mockFileClient = new MockFileClient();
$myClass = new MyClass($mockFileClient);
$myClass->createFile('/test/path');

print_r($mockFileClient->calledOpen . PHP_EOL);
print_r($mockFileClient->calledExists . PHP_EOL);

So we are creating an interface (FileClientInterface) and two implementing classes, one for production one for tests (the one with mock). The testing implementation just increments a counter when it's method are called. This way we decouple the actual I/O methods in the test.

Dan Ionescu
  • 3,135
  • 1
  • 12
  • 17