vfsstream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsstream.pdfvfsstream a better approach...

39
vfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Upload: others

Post on 08-Oct-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

vfsStream

A better approach for file system dependent tests

Frank Kleine, 1&1 Internet AG

Page 2: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 3: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 4: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 5: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 6: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 7: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 8: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

T-Shirt available at zazzle.de

(no, I'm not paid for this)

Page 9: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 10: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

(Obligatory Zen-Style image)

Page 11: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 12: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

AND NOW FOR

SOMETHING

COMPLETELY DIFFERENT

Page 13: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Unit tests

� I assume you do unit tests

�Testing file system relatedfunctions/methods can be hard

�setup(), teardown(), and unusual scenarios

Page 14: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Basic example: a method to test

class Example {public function __construct($id) {

$this->id = $id;}

public function setDirectory($dir) {$this->dir = $dir . '/' . $this->id;if (file_exists($this->dir) === false) {

mkdir($this->dir, 0700, true);}

}…

}

Page 15: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Basic example: traditional test

$DIR = dirname(__FILE__);public function setUp() {

if (file_exists($DIR . '/id')) {rmdir($DIR . '/id');

}}public function tearDown() {

if (file_exists($DIR . '/id')) {rmdir($DIR . '/id');

}}public function testDirectoryIsCreated() {

$example = new Example('id');$this->assertFalse(file_exists($DIR . '/id'));$example->setDirectory($DIR);$this->assertTrue(file_exists($DIR . '/id'));

}

Page 16: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Basic example: vfsStream test

public function setUp() {vfsStreamWrapper::register();$root = new vfsStreamDirectory('aDir');vfsStreamWrapper::setRoot($root);

}

public function testDirectoryIsCreated() {$url = vfsStream::url('aDir/id');$example = new Example('id');$this->assertFalse(file_exists($url));$example->setDirectory(vfsStream::url('aDir'));$this->assertTrue(file_exists($url));

}

Page 17: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Advantages

�Cleaner tests

�Nothing happens on disc, all operations arememory only

�More control over file system environment

Page 18: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

How does this work?

�PHP stream wrapper

�Allows you to invent your own url protocol

�Whenever a vfs:// url is given to a filesystem function PHP calls back theregistered stream implementation for thisprotocol

�Works well for most of file system functions

Page 19: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

What does not work?

� File system functions working with pure filenames only:� realpath()

� touch()

� File system function without stream wrappersupport:� chmod()

� chown()

� chgrp()

� ext/zip does not support userland streamwrappers

Page 20: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Example with file mode

class Example {public function __construct($id, $mode = 0700) {

$this->id = $id;$this->mode = $mode;

}

public function setDirectory($dir) {$this->dir = $dir . '/' . $this->id;if (file_exists($this->dir) === false) {

mkdir($this->directory, $this->mode, true);}

}…

}

Page 21: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Example with file mode, cont.

$DIR = dirname(__FILE__);public function testDirDefaultFilePermissions() {

$example = new Example('id');$example->setDirectory($DIR);if (DIRECTORY_SEPARATOR === '\\') {

$this->assertEquals(40777, decoct(fileperms($DIR . ' /id')));} else {

$this->assertEquals(40700, decoct(fileperms($DIR . ' /id')));}

}public function testDirDifferentFilePermissions() {

$example = new Example('id', 0755);$example->setDirectory($DIR);if (DIRECTORY_SEPARATOR === '\\') {

$this->assertEquals(40777, decoct(fileperms($DIR . ' /id')));} else {

$this->assertEquals(40755, decoct(fileperms($DIR . ' /id')));}

}

Page 22: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Example with file mode, cont.

public function setUp() {vfsStreamWrapper::register();$this->root = new vfsStreamDirectory('aDir');vfsStreamWrapper::setRoot($this->root);

}

public function testDirDefaultFilePermissions() {$example = new Example('id');$example->setDirectory(vfsStream::url('aDir'));$this->assertEquals(0700,

$this->root->getChild('id')->getPermissions()); }

public function testDirDifferentFilePermissions() {$example = new Example('id', 0755);$example->setDirectory(vfsStream::url('aDir'));$this->assertEquals(0755,

$this->root->getChild('id')->getPermissions());}

Page 23: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Advantages

� Independent of operating system a test isrunning on

� Intentions of test cases become more clear

�Easier to understand

Page 24: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files

class RssFeedController {public function __construct($configPath) {

$feeds = Properties::fromFile($configPath . '/rss-fee ds.ini')->getSection('feeds', array());

if (count($feeds) === 0) {throw new ConfigurationException();

}

$this->routeName = valueFromRequest();if (null === $this->routeName) {

// no special feed requested, use first configured onereset($feeds);$this->routeName = key($feeds);

}}…

}

Page 25: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files, cont.

public function setUp() {vfsStreamWrapper::register();$root = new vfsStreamDirectory('config');vfsStreamWrapper::setRoot($root);$this->configFile = vfsStream::newFile('rss-feeds.in i')

->at($root);}

/*** @test* @expectedException FileNotFoundException**/

public function loadFeedsFailsIfFeedConfigFileDoesNot Exist() {$example = new RssFeedController(vfsStream::url('does NotExist'));

}

Page 26: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files, cont. 2

/*** @test* @expectedException ConfigurationException**/

public function noFeedsSectionConfiguredThrowsExcepti on() {$this->configFile->setContent('');

$example = new RssFeedController(vfsStream::url('conf ig'));}

Page 27: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files, cont. 3

/*** @test* @expectedException ConfigurationException**/

public function noFeedsConfiguredThrowsException() {$this->configFile->setContent('[feeds]');

$example = new RssFeedController(vfsStream::url('conf ig'));}

Page 28: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files, cont. 4

/*** @test**/

public function selectsFirstFeedIfNoneGivenWithReques tValue() {$this->configFile->setContent('[feeds]\n

default = "org::stubbles::test::xml::rss::DefaultFee d"');

$example = new RssFeedController(vfsStream::url('conf ig'));

// assertions that the default feed was selected

…}

Page 29: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Different config files, cont. 5

/*** @test**/

public function selectsOtherFeedBasedOnRequestValue() {$this->configFile->setContent("[feeds]\n

default = \"org::stubbles::test::xml::rss::DefaultFe ed\"\nother = \"org::stubbles::test::xml::rss::OtherFeed\" \n");

$example = new RssFeedController(vfsStream::url('conf ig'));

// assertions that the other feed was selected

…}

Page 30: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Advantages

�Concentrate on the test, not on config files

�Everything is in the test, no test relatedinformation in external files

�Simple to set up

�Create different test scenarios on the fly

Page 31: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

vfsStream 0.7.0

�Considers file permissions correctly

�Allows to write tests to check how yourapplication responds to incorrect filepermissions

�To be released in the next days

�Probably buggy :->

Page 32: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

File permissions

class Example {public function writeConfig($config, $configFile) {

file_put_contents($configFile, serialize($config));}

…}

Page 33: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

File permissions, the tests

/*** @test*/

public function normalTest() {vfsStreamWrapper::setRoot(vfsStream::newDirectory(' exampleDir'));$example = new FilePermissionsExample();$example->writeConfig(array('foo' => 'bar'),

vfsStream::url('exampleDir/writable.ini'));

// assertions here}

Page 34: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

File permissions, another test

/*** @test*/

public function directoryNotWritable() {vfsStreamWrapper::setRoot(

vfsStream::newDirectory('exampleDir', 0444));$example = new FilePermissionsExample();$example->writeConfig(array('foo ' => 'bar'),

vfsStream::url('exampleDir/config.ini'));

}

Page 35: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Advantages

�Allows simple tests you would not do otherwise because they are too hard to do

�Helps to find probably buggy code or codewhich behaves not user friendly

�Makes your application more failsafe

Page 36: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 37: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG

Ressources

�http://vfs.bovigo.org/

�http://php.net/class.streamwrapper

�Twitter

�@bovigo

�@mikey179

Page 38: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG
Page 39: vfsStream - talks.frankkleine.detalks.frankkleine.de/ipc_vfsStream.pdfvfsStream A better approach for file system dependent tests Frank Kleine, 1&1 Internet AG