Using PHPUnit with Gaufrette to unit test IO-dependent behaviour

Recently I wanted to test some class which uses heavily IO. I was refactoring some legacy code depending on file_get_contents and other file functions. To allow easy testing I added Gaufrette library and replaced all native PHP functions operating on IO.

<?php

use Gaufrette\Filesystem;

class Generator
{
    /**
     * @var Filesystem
     */

    protected $filesystem;

    public function __construct(Filesystem $filesystem)
    {
         $this->filesystem = $filesystem;
    }
   
    public function someOperation()
    {
         $content = $this->filesystem->get('myfile')->getContent();
         // ... some operation
         return $result;
    }
}

Unit test for our class

<?php

use Gaufrette\Adapter\InMemory;
use Gaufrette\Filesystem;

class GeneratorTest extends \PHPUnit_Framework_TestCase
{
    public function testSomeOperation()
    {
         $adapter = new InMemory(array('myfile' => 'content'));
         $filesystem = new Filesystem($adapter);
         $generator = new Generator($filesystem);

         $result = $generator->someOperation();

         // assert something with result
    }
}

When using our generator in the code, we can easily change the adapter to support local filesystem, remote and cloud ones (full list of adapters here).

<?php

use Gaufrette\Adapter\Local; // using local filesystem
use Gaufrette\Filesystem;

class GeneratorRunner
{
    public function run()
    {
         $adapter = new Local('directory');
         $filesystem = new Filesystem($adapter);
         $generator = new Generator($filesystem);

         $result = $generator->someOperation();

    }
}

Of course you can use Dependency Injection which allows for even simpler adapter change.

parameters:
  generator.class
: Generator
  filesystem.class
: Gaufrette\Filesystem
  adapter.class
:   Gaufrette\Adapter\Local
  adapter.arguments
: ["directory"]

services
:
  generator
:
    class
: %generator.class%
    arguments
: ["@filesystem"]
  filesystem
:
    class
: %filesystem.class%
    arguments
: ["@adapter"]
  adapter
:
    class
: %adapter.class%
    arguments
: %adapter.arguments%
// ...
$generator = $container->get('generator');
$generator->someAction();