VfsStream effective filesystem mocking Sebastian Marek
Prerequisites some experience in unit testing
basic knowledge of PHPUnit
External dependencies database
SOAP
3 rd  party
Filesystem Easy to set up
Many different ways
Not a dependency REALLY!?
Learning by example <?php class  Config { private  $filename ; public function  __construct($filename) { if  (!is_file($filename)) { throw new  Exception( &quot;Can't set the filename - it doesn't exists!&quot; ); } $this -> filename  = $filename; } public function  fetchConfig() { return  parse_ini_file( $this -> filename ,  true ); } public function  createCacheDir($directory) { if  (!is_dir($directory)) { return  mkdir($directory, 0700,  true ); } return   false ; } }
guerrillas setUp and tearDown
prepare upfront http://picasaweb.google.co.uk/catof.airsoft/PartisanRusse#5480348761446205778
guerrillas test case <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { private  $cacheDir  =  '/tmp/cache' ; private  $configFile  =  '/tmp/config.ini' ; // (...) /** * @covers Config::__construct */ public function  testConstructorSetsConfigFilename() { $config =  new  Config( $this -> configFile ); $this ->assertAttributeEquals( $this -> configFile ,  'filename' , $config); } // (...) }
guerrillas test case <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { private  $cacheDir  =  '/tmp/cache' ; private  $configFile  =  '/tmp/config.ini' ; // (...) /** * @covers Config::__construct */ public function  testConstructorThrowsExceptionWithInvalidConfigFile() { $configFile =  '/share/usr/config.ini' ; $this ->setExpectedException( 'Exception' ,  &quot;Can't set the filename - it doesn't exists!&quot; ); $config =  new  Config($configFile); } // (...) }
guerrillas test case <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { private  $cacheDir  =  '/tmp/cache' ; private  $configFile  =  '/tmp/config.ini' ; protected function  setUp() { $content =  &quot; [section1] key1=value1 key2=value3 [section2] key3=value3 &quot; ; file_put_contents( $this -> configFile , $content); if  (!is_dir( $this -> cacheDir )) { mkdir( $this -> cacheDir ); } } // (...) }
guerrillas test case class  ConfigTest  extends  PHPUnit_Framework_TestCase { private  $cacheDir  =  '/tmp/cache' ; private  $configFile  =  '/tmp/config.ini' ; // (...) protected function  tearDown() { if  (is_dir( $this -> cacheDir )) { rmdir( $this -> cacheDir ); } unlink( $this -> configFile ); } // (...) }
Modern warfare vfsStream mocks away filesystem
uses php stream wrapper http://picasaweb.google.co.uk/dawson.jerome/MCASMiramarAirShow2007#5121760885155756402
vfsStream installation #> pear channel-discover pear.php-tools.net #> pear install pat/vfsStream-alpha
vfsStream - register <?php require_once  'vfsStream/vfsStream.php' // (...) protected  function  setUp() { vfsStream:: setup ( 'root' ); } // (...)
vfsStream & PHPUnit <?php class  SomeTest  extends  PHPUnit_Framework_TestCase { protected function  setVfsStream() { @ include_once  'vfsStream/vfsStream.php' ; if  (!class_exists( 'vfsStreamWrapper' )) { $this ->markTestSkipped( 'vfsStream is not available - skipping' ); }  else  { vfsStream:: setup ( 'root' ); } } public function  testSomething() { $this ->setVfsStream(); //(...) } }
vfsStream – working with files <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { /** * @covers Config::__construct */ public function  testConstructorSetsConfigFilename() { $this ->setVfsStream(); vfsStream::newFile( 'config.ini' )->at(vfsStreamWrapper::getRoot()); $config =  new  Config(vfsStream::url( 'root/config.ini' )); $this ->assertAttributeEquals(vfsStream::url( 'root/config.ini' ),  'filename' , $config); } // (...) }
vfsStream – working with files <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { /** * @covers Config::__construct */ public function  testConstructorThrowsExceptionWithInvalidConfigFile() { $this ->setVfsStream(); $this ->setExpectedException( 'Exception' ,  &quot;Can't set the filename - it doesn't exists!&quot; ); $config =  new  Config(vfsStream::url( 'root/config.ini' )); } // (...) }
vfsStream url <?php // (...) public function dumpVfsStreamUrl () { $this ->setVfsStream(); echo  vfsStream::url( 'root/config.ini' ); // vfs://root/config.ini } // (...) }
vfsStream – working with dirs <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { /** * @covers Config::createCacheDir */ public function  testCreateCacheDirIfItDoesntExists() { $this ->setVfsStream(); $this ->assertFileNotExists(vfsStream:: url ( 'root/cache' )); vfsStream:: newFile ( 'config.ini' )->at(vfsStreamWrapper:: getRoot ()); $config =  new  Config(vfsStream:: url ( 'root/config.ini' )); $config->createCacheDir(vfsStream:: url ( 'root/cache' )); $this ->assertFileExists(vfsStream:: url ( 'root/cache' )); } // (...) }
vfsStream – working with dirs <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { /** * @covers Config::createCacheDir */ public function  testDontCreateCacheDirIfItExists() { $this ->setVfsStream(); vfsStream:: newFile ( 'config.ini' )->at(vfsStreamWrapper:: getRoot ()); vfsStream:: newDirectory ( 'cache' )->at(vfsStreamWrapper:: getRoot ()); $this ->assertFileExists(vfsStream:: url ( 'root/cache' )); $config =  new  Config(vfsStream:: url ( 'root/config.ini' )); $result = $config->createCacheDir(vfsStream:: url ( 'root/cache' )); $this ->assertFalse($result); } // (...) }
vfsStream – working with content <?php class  ConfigTest  extends  PHPUnit_Framework_TestCase { /** * @covers Config::fetchConfig */ public function  testFetchConfigReturnsArray() { $this ->setVfsStream(); $content =  << < 'EOT' [section1] key1 = value1 [section2] key2 = value2 [section3] key3 = value3 EOT; vfsStream:: newFile ( 'config.ini' )->withContent($content) ->at(vfsStreamWrapper:: getRoot ()); $config =  new  Config(vfsStream:: url ( 'root/config.ini' )); $result = $config->fetchConfig(); $this ->assertTrue(is_array($result)); $this ->assertEquals(3, count($result)); $this ->assertEquals( array ( 'key1'  =>  'value1' ), $result[ 'section1' ]); $this ->assertEquals( array ( 'key2'  =>  'value2' ), $result[ 'section2' ]); $this ->assertEquals( array ( 'key3'  =>  'value3' ), $result[ 'section3' ]); } }
vfsStream – file perms new  vfsStreamFile( 'app.ini' , 0644); new  vfsStreamDirectory( '/home' , 0755); vfsStream:: newFile ( 'app.ini' , 0640); vfsStream:: newDirectory ( '/tmp/cache' , 0755); $vfsStreamFile->chmod(0664); $vfsStreamDirectory->chmod(0700);
vfsStream – file ownership $vfsStreamFile->chown(vfsStream:: OWNER_USER_2 ); $vfsStreamDirectory->chown(vfsStream:: OWNER_ROOT ); $vfsStreamFile->chgrp(vfsStream:: GROUP_USER_2 ); $vfsStreamDirectory->chgrp(vfsStream:: GROUP_ROOT );
vfsStream – file ownership vfsStream:: OWNER_ROOT  // 0
vfsStream:: OWNER_USER_1 // 1
vfsStream:: OWNER_USER_2 // 2 vfsStream:: GROUP_ROOT  // 0
vfsStream:: GROUP_USER_1 // 1

vfsStream - effective filesystem mocking

  • 1.
    VfsStream effective filesystemmocking Sebastian Marek
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
    3 rd party
  • 7.
  • 8.
  • 9.
  • 10.
    Learning by example<?php class Config { private $filename ; public function __construct($filename) { if (!is_file($filename)) { throw new Exception( &quot;Can't set the filename - it doesn't exists!&quot; ); } $this -> filename = $filename; } public function fetchConfig() { return parse_ini_file( $this -> filename , true ); } public function createCacheDir($directory) { if (!is_dir($directory)) { return mkdir($directory, 0700, true ); } return false ; } }
  • 11.
  • 12.
  • 13.
    guerrillas test case<?php class ConfigTest extends PHPUnit_Framework_TestCase { private $cacheDir = '/tmp/cache' ; private $configFile = '/tmp/config.ini' ; // (...) /** * @covers Config::__construct */ public function testConstructorSetsConfigFilename() { $config = new Config( $this -> configFile ); $this ->assertAttributeEquals( $this -> configFile , 'filename' , $config); } // (...) }
  • 14.
    guerrillas test case<?php class ConfigTest extends PHPUnit_Framework_TestCase { private $cacheDir = '/tmp/cache' ; private $configFile = '/tmp/config.ini' ; // (...) /** * @covers Config::__construct */ public function testConstructorThrowsExceptionWithInvalidConfigFile() { $configFile = '/share/usr/config.ini' ; $this ->setExpectedException( 'Exception' , &quot;Can't set the filename - it doesn't exists!&quot; ); $config = new Config($configFile); } // (...) }
  • 15.
    guerrillas test case<?php class ConfigTest extends PHPUnit_Framework_TestCase { private $cacheDir = '/tmp/cache' ; private $configFile = '/tmp/config.ini' ; protected function setUp() { $content = &quot; [section1] key1=value1 key2=value3 [section2] key3=value3 &quot; ; file_put_contents( $this -> configFile , $content); if (!is_dir( $this -> cacheDir )) { mkdir( $this -> cacheDir ); } } // (...) }
  • 16.
    guerrillas test caseclass ConfigTest extends PHPUnit_Framework_TestCase { private $cacheDir = '/tmp/cache' ; private $configFile = '/tmp/config.ini' ; // (...) protected function tearDown() { if (is_dir( $this -> cacheDir )) { rmdir( $this -> cacheDir ); } unlink( $this -> configFile ); } // (...) }
  • 17.
    Modern warfare vfsStreammocks away filesystem
  • 18.
    uses php streamwrapper http://picasaweb.google.co.uk/dawson.jerome/MCASMiramarAirShow2007#5121760885155756402
  • 19.
    vfsStream installation #>pear channel-discover pear.php-tools.net #> pear install pat/vfsStream-alpha
  • 20.
    vfsStream - register<?php require_once 'vfsStream/vfsStream.php' // (...) protected function setUp() { vfsStream:: setup ( 'root' ); } // (...)
  • 21.
    vfsStream & PHPUnit<?php class SomeTest extends PHPUnit_Framework_TestCase { protected function setVfsStream() { @ include_once 'vfsStream/vfsStream.php' ; if (!class_exists( 'vfsStreamWrapper' )) { $this ->markTestSkipped( 'vfsStream is not available - skipping' ); } else { vfsStream:: setup ( 'root' ); } } public function testSomething() { $this ->setVfsStream(); //(...) } }
  • 22.
    vfsStream – workingwith files <?php class ConfigTest extends PHPUnit_Framework_TestCase { /** * @covers Config::__construct */ public function testConstructorSetsConfigFilename() { $this ->setVfsStream(); vfsStream::newFile( 'config.ini' )->at(vfsStreamWrapper::getRoot()); $config = new Config(vfsStream::url( 'root/config.ini' )); $this ->assertAttributeEquals(vfsStream::url( 'root/config.ini' ), 'filename' , $config); } // (...) }
  • 23.
    vfsStream – workingwith files <?php class ConfigTest extends PHPUnit_Framework_TestCase { /** * @covers Config::__construct */ public function testConstructorThrowsExceptionWithInvalidConfigFile() { $this ->setVfsStream(); $this ->setExpectedException( 'Exception' , &quot;Can't set the filename - it doesn't exists!&quot; ); $config = new Config(vfsStream::url( 'root/config.ini' )); } // (...) }
  • 24.
    vfsStream url <?php// (...) public function dumpVfsStreamUrl () { $this ->setVfsStream(); echo vfsStream::url( 'root/config.ini' ); // vfs://root/config.ini } // (...) }
  • 25.
    vfsStream – workingwith dirs <?php class ConfigTest extends PHPUnit_Framework_TestCase { /** * @covers Config::createCacheDir */ public function testCreateCacheDirIfItDoesntExists() { $this ->setVfsStream(); $this ->assertFileNotExists(vfsStream:: url ( 'root/cache' )); vfsStream:: newFile ( 'config.ini' )->at(vfsStreamWrapper:: getRoot ()); $config = new Config(vfsStream:: url ( 'root/config.ini' )); $config->createCacheDir(vfsStream:: url ( 'root/cache' )); $this ->assertFileExists(vfsStream:: url ( 'root/cache' )); } // (...) }
  • 26.
    vfsStream – workingwith dirs <?php class ConfigTest extends PHPUnit_Framework_TestCase { /** * @covers Config::createCacheDir */ public function testDontCreateCacheDirIfItExists() { $this ->setVfsStream(); vfsStream:: newFile ( 'config.ini' )->at(vfsStreamWrapper:: getRoot ()); vfsStream:: newDirectory ( 'cache' )->at(vfsStreamWrapper:: getRoot ()); $this ->assertFileExists(vfsStream:: url ( 'root/cache' )); $config = new Config(vfsStream:: url ( 'root/config.ini' )); $result = $config->createCacheDir(vfsStream:: url ( 'root/cache' )); $this ->assertFalse($result); } // (...) }
  • 27.
    vfsStream – workingwith content <?php class ConfigTest extends PHPUnit_Framework_TestCase { /** * @covers Config::fetchConfig */ public function testFetchConfigReturnsArray() { $this ->setVfsStream(); $content = << < 'EOT' [section1] key1 = value1 [section2] key2 = value2 [section3] key3 = value3 EOT; vfsStream:: newFile ( 'config.ini' )->withContent($content) ->at(vfsStreamWrapper:: getRoot ()); $config = new Config(vfsStream:: url ( 'root/config.ini' )); $result = $config->fetchConfig(); $this ->assertTrue(is_array($result)); $this ->assertEquals(3, count($result)); $this ->assertEquals( array ( 'key1' => 'value1' ), $result[ 'section1' ]); $this ->assertEquals( array ( 'key2' => 'value2' ), $result[ 'section2' ]); $this ->assertEquals( array ( 'key3' => 'value3' ), $result[ 'section3' ]); } }
  • 28.
    vfsStream – fileperms new vfsStreamFile( 'app.ini' , 0644); new vfsStreamDirectory( '/home' , 0755); vfsStream:: newFile ( 'app.ini' , 0640); vfsStream:: newDirectory ( '/tmp/cache' , 0755); $vfsStreamFile->chmod(0664); $vfsStreamDirectory->chmod(0700);
  • 29.
    vfsStream – fileownership $vfsStreamFile->chown(vfsStream:: OWNER_USER_2 ); $vfsStreamDirectory->chown(vfsStream:: OWNER_ROOT ); $vfsStreamFile->chgrp(vfsStream:: GROUP_USER_2 ); $vfsStreamDirectory->chgrp(vfsStream:: GROUP_ROOT );
  • 30.
    vfsStream – fileownership vfsStream:: OWNER_ROOT // 0
  • 31.
  • 32.
    vfsStream:: OWNER_USER_2 //2 vfsStream:: GROUP_ROOT // 0
  • 33.
  • 34.
  • 35.
  • 36.
    chmod() , chown() , chgrp(), tempnam() doesn't work with streams
  • 37.
    no support for fileatime() and filectime()
  • 38.
    realpath() and touch() does not work with any other URLs than pure filenames
  • 39.
  • 40.
    vfsStream PHPUnit Helpersimplified integration with PHPUnit
  • 41.
    skips when vfsStreamnot installed
  • 42.
  • 43.
    vfsStream PHPUnit Helper<?php class MyClassTest extends PHPUnit_Framework_TestCase { // (...) /** * - It will skip the test if vfsStream is not installed * - It will register vfsStream in default root directory called 'root' * - creates 'tmp' directory in root directory */ public function testCreateDirectoryInDefaultRootDirectory() { $vfsStreamWrapper = new vfsStreamHelper_Wrapper( $this ); $vfsStreamWrapper->createDirectory( &quot;tmp&quot; ); $this ->assertFileExists(vfsStream:: url ( 'root/tmp' )); } // (...) }
  • 44.
    vfsStream PHPUnit Helper<?php class MyClassTest extends PHPUnit_Framework_TestCase { // (...) /** * - It will skip the test if vfsStream is not installed * - It will register vfsStream in default root directory called 'root' * - creates empty 'myFile.txt' file in root directory */ public function testCreateEmptyFileInDefaultRootDirectory() { $vfsStreamWrapper = new vfsStreamHelper_Wrapper( $this ); $vfsStreamWrapper->createFile( &quot;myFile.txt&quot; ); $this ->assertFileExists(vfsStream:: url ( 'root/myFile.txt' )); } // (...) }
  • 45.
    vfsStream PHPUnit Helper<?php class MyClassTest extends PHPUnit_Framework_TestCase { // (...) /** * - It will skip the test if vfsStream is not installed * - It will register vfsStream in root directory called 'myDir' * - creates 'home' directory in root directory */ public function testCreateDirectoryInCustomRootDirectory() { $vfsStreamWrapper = new vfsStreamHelper_Wrapper( $this , 'myDir' ); $vfsStreamWrapper->createDirectory( &quot;home&quot; ); $this ->assertFileExists(vfsStream:: url ( 'myDir/home' )); } // (...) }
  • 46.
    vfsStream PHPUnit Helper<?php class MyClassTest extends PHPUnit_Framework_TestCase { // (...) /** * - It will skip the test if vfsStream is not installed * - It will register vfsStream in default root directory called 'root' * - creates directory in different possible ways */ public function testDifferentWaysOfCreatingDirectories() { $vfsStreamWrapper = new vfsStreamHelper_Wrapper( $this ); // create a single directory $vfsStreamWrapper->createDirectory( &quot;tmp&quot; ); // create nested directories $vfsStreamWrapper->createDirectory( &quot;home/proofek/downloads&quot; ); // create a directory using vfsStreamHelper_Directory in default root $vfsStreamWrapper->createDirectory( new vfsStreamHelper_Directory( 'etc' )); // create a directory using vfsStreamHelper_Directory in a subdirectory $vfsStreamWrapper->createDirectory( new vfsStreamHelper_Directory( 'init.d' , 'etc' ) ); // (...)
  • 47.
    vfsStream PHPUnit Helper// (...) // create multiple directories $vfsStreamWrapper->createDirectory( array ( new vfsStreamHelper_Directory( 'user1' , 'home' ), new vfsStreamHelper_Directory( 'user2' , 'home' ), new vfsStreamHelper_Directory( 'usr' ), ) ); } // (...) }
  • 48.
    vfsStream PHPUnit Helper<?php class MyClassTest extends PHPUnit_Framework_TestCase { // (...) /** * - It will skip the test if vfsStream is not installed * - It will register vfsStream in default root directory called 'root' * - creates files in different possible ways */ public function testDifferentWaysOfCreatingFiles() { $vfsStreamWrapper = new vfsStreamHelper_Wrapper( $this ); // create a single empty file in default root directory $vfsStreamWrapper->createFile( &quot;myFile.txt&quot; ); // create a single empty file using vfsStreamHelper_File in default root $vfsStreamWrapper->createFile( new vfsStreamHelper_File( 'anotherFile.txt' ) ); // create a single file with contents using vfsStreamHelper_File in default root $fileContent = &quot;First line in the file\nSecond line in the file\n&quot; ; $vfsStreamWrapper->createFile( new vfsStreamHelper_File( 'thirdFile.txt' , $fileContent) ); // (...)
  • 49.
    vfsStream PHPUnit Helper// (...) // create a single file with contents using vfsStreamHelper_File in // a subdirectory $fileContent = &quot;First line in the file\nSecond line in the file\n&quot; ; $vfsStreamWrapper->createDirectory( &quot;tmp&quot; ); $vfsStreamWrapper->createFile( new vfsStreamHelper_File( 'file.txt' , $fileContent, 'tmp' ) ); // create multiple files $vfsStreamWrapper->createDirectory( &quot;etc&quot; ); $vfsStreamWrapper->createFile( array ( new vfsStreamHelper_File( 'file1.txt' , 'some content' , 'etc' ), new vfsStreamHelper_File( 'file2.txt' , null , 'etc' ), new vfsStreamHelper_File( 'file3.txt' ), ) ); } // (...) }
  • 50.
    Need help? Resourceshttp://code.google.com/p/bovigo/wiki/vfsStream
  • 51.
  • 52.
  • 53.
    github - http://github.com/proofek

Editor's Notes

  • #22 Not full support Helps test things like is_readable(), is_writable() or is_executable() Default file mode 0666 Default dir mode 0777 Subdirs get parents perms