Untestable Code

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    9 Favorites, 1 Group & 1 Event

    Untestable Code - Presentation Transcript

    1. Untestable Code Sebastian Bergmann May 20th 2009
    2. Who I Am  Sebastian Bergmann  Involved in the PHP project since 2000  Creator of PHPUnit  Co-Founder and Principal Consultant with thePHP.cc
    3. What makes code untestable? Most people say Real issues  Make things private  Mix new with logic  Use final keyword  Look for things  Write long methods  Work in constructor  ...  Singletons  Global state  Static methods This slide contains material by Miško Hevery
    4. Constructor Issues <?php Mixing object graph class Document { construction with work private $html; public function __construct($url) { $client = new HttpClient; $this->html = $client->get($url); } } ?> Doing work in the constructor This slide contains material by Miško Hevery
    5. Constructor Issues <?php class Document { private $html; public function __construct($url) { $client = new HttpClient; $this->html = $client->get($url); } } ?>  A Document object cannot be isolated from its dependency on an HttpClient object as we cannot pass a Test Double to the constructor  Support for Class Posing in PHP could help  But we should solve the design problem This slide contains material by Miško Hevery
    6. Solving the Design Problem Step 1: Do not mix new with logic <?php class Document { private $html; public function __construct(HttpClient $client, $url) { $this->html = $client->get($url); } } ?> Doing work in the constructor This slide contains material by Miško Hevery
    7. Interlude Test Doubles Replacing a component on which the tested component depends with a \"test-specific equivalent\". <?php class DocumentTest extends PHPUnit_Framework_TestCase { public function testSomething() { $stub = $this->getMock('HttpClient'); $stub->expects($this->any()) ->method('get') ->will($this->returnValue('<html>...</html>')); $document = new Document($stub, 'url'); // ... } } ?>
    8. Interlude Test Doubles  Dummy Not the real object  Fake Usable for testing but not for real job  Stub Fake that returns canned data  Spy Stub that records called methods, etc.  Mock Spy with expectations
    9. Solving the Design Problem Step 2: Do not perform work in the constructor <?php class Document { private $html; public function __construct($html) { $this->html = $html; } } This slide contains material by Miško Hevery
    10. Solving the Design Problem Step 2: Do not perform work in the constructor <?php class Document { private $html; public function __construct($html) { $this->html = $html; } } class DocumentFactory { public function __construct(HttpClient $client) { $this->client = $client; } public function build($url) { return new Document($this->client->get($url)); } } This slide contains material by Miško Hevery
    11. Solving the Design Problem Step 2: Do not perform work in the constructor <?php class Document { private $html; public function __construct($html) { $this->html = $html; } } class Printer { public function printDocument(Document $document) { class DocumentFactory { // ... public function __construct(HtmlClient $client) { } } $this->client = $client; } public function build($url) { return new Document($this->client->get($url)); } } This slide contains material by Miško Hevery
    12. Global State  Global variables  Static variables in classes  Static variables in functions and methods  Persistent Data  Database  Filesystem  Distributed Data  memcached
    13. Global State Global Variables in PHP  A variable $foo declared in the global scope is stored in $GLOBALS['foo'].  $GLOBALS is a so-called superglobal.  Superglobals are built-in variables that are available in all scopes.  $GLOBALS['foo'] can be used to access the global variable $foo in the scope of a function or method  global $foo; can be used to create a local variable with a reference to the global variable.
    14. Global State Superglobals in PHP $GLOBALS = array( 'GLOBALS' => &$GLOBALS, '_ENV' => array(), '_POST' => array(), '_GET' => array(), '_COOKIE' => array(), '_SERVER' => array(), '_FILES' => array(), '_REQUEST' => array() );
    15. Global State Global Variables and PHPUnit <?php class GlobalsTest extends PHPUnit_Framework_TestCase { public function testOne() { $GLOBALS['foo'] = 'bar'; $this->assertEquals('bar', $GLOBALS['foo']); } public function testTwo() { $this->assertFalse(isset($GLOBALS['foo'])); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    16. Global State Global Variables and PHPUnit <?php /** * @backupGlobals disabled */ class GlobalsTest extends PHPUnit_Framework_TestCase { public function testOne() { $GLOBALS['foo'] = 'bar'; $this->assertEquals('bar', $GLOBALS['foo']); } public function testTwo() { $this->assertTrue(isset($GLOBALS['foo'])); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    17. Global State Global Variables and PHPUnit <?php /** * @backupGlobals disabled */ class GlobalsTest extends PHPUnit_Framework_TestCase { /** * @backupGlobals enabled */ public function testOne() { $GLOBALS['foo'] = 'bar'; $this->assertEquals('bar', $GLOBALS['foo']); } public function testTwo() { $this->assertFalse(isset($GLOBALS['foo'])); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    18. Global State Global Variables and PHPUnit <?php class GlobalsTest extends PHPUnit_Framework_TestCase { protected $backupGlobalsBlacklist = array('foo'); public function testOne() { $GLOBALS['foo'] = 'bar'; $this->assertEquals('bar', $GLOBALS['foo']); } public function testTwo() { $this->assertTrue(isset($GLOBALS['foo'])); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    19. Global State Global Variables and PHPUnit  Default Behaviour  Backup $GLOBALS before the execution of a test  Run the test  Restore $GLOBALS after the execution of a test  $backupGlobals = FALSE  Do not backup and restore $GLOBALS  $backupGlobalsBlacklist = array()  Backup and restore $GLOBALS but not the variables listed in the array
    20. Global State Static Attributes and PHPUnit <?php class Singleton { private static $uniqueInstance = NULL; protected function __construct() {} private final function __clone() {} public static function getInstance() { if (self::$uniqueInstance === NULL) { self::$uniqueInstance = new Singleton; } return self::$uniqueInstance; } } ?>
    21. Global State Static Attributes and PHPUnit <?php class SingletonTest extends PHPUnit_Framework_TestCase { public function testFirstCallToGetInstance() { return Singleton::getInstance(); } /** * @depends testFirstCallToGetInstance */ public function testSecondCallToGetInstance($singleton) { $this->assertNotSame( $singleton, Singleton::getInstance() ); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    22. Global State Static Attributes and PHPUnit <?php /** * @backupStaticAttributes disabled */ class SingletonTest extends PHPUnit_Framework_TestCase { public function testFirstCallToGetInstance() { return Singleton::getInstance(); } /** * @depends testFirstCallToGetInstance */ public function testSecondCallToGetInstance($singleton) { $this->assertSame( $singleton, Singleton::getInstance() ); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    23. Global State Static Attributes and PHPUnit <?php class SingletonTest extends PHPUnit_Framework_TestCase { /** * @backupStaticAttributes disabled */ public function testFirstCallToGetInstance() { return Singleton::getInstance(); } /** * @depends testFirstCallToGetInstance */ public function testSecondCallToGetInstance($singleton) { $this->assertSame( $singleton, Singleton::getInstance() ); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    24. Global State Static Attributes and PHPUnit <?php class SingletonTest extends PHPUnit_Framework_TestCase { protected $backupStaticAttributesBlacklist = array( 'Singleton' => array('uniqueInstance') ); public function testFirstCallToGetInstance() { return Singleton::getInstance(); } /** * @depends testFirstCallToGetInstance */ public function testSecondCallToGetInstance($singleton) { $this->assertSame( $singleton, Singleton::getInstance() ); } } ?> sb@ubuntu ~ % phpunit GlobalsTest PHPUnit 3.4.0 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 2 assertions)
    25. Global State Static Attributes and PHPUnit  Default Behaviour  Backup static attributes of user-defined classes before the execution of a test  Run the test  Restore static attributes of user-defined classes after the execution of a test  $backupStaticAttributes = FALSE  Do not backup and restore static attributes of user- defined classes  $backupStaticAttributesBlacklist = array()  Backup and restore static attributes of user-defined classes but not the variables listed in the array
    26. Global State Filesystem <?php class Example { protected $id; protected $directory; public function __construct($id) { $this->id = $id; } public function setDirectory($directory) { $this->directory = $directory . DIRECTORY_SEPARATOR . $this->id; if (!file_exists($this->directory)) { mkdir($this->directory, 0700, TRUE); } } } ?>
    27. Global State Filesystem <?php require_once 'Example.php'; class ExampleTest extends PHPUnit_Framework_TestCase { protected function setUp() { 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')); } protected function tearDown() { if (file_exists(__DIR__ . '/id')) { rmdir(__DIR__ . '/id'); } } } ?>
    28. Global State Mocking the filesystem with vfsStream <?php require_once 'vfsStream/vfsStream.php'; require_once 'Example.php'; class ExampleTest extends PHPUnit_Framework_TestCase { public function setUp() { vfsStreamWrapper::register(); vfsStreamWrapper::setRoot(new vfsStreamDirectory('exampleDir')); } public function testDirectoryIsCreated() { $example = new Example('id'); $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('id')); $example->setDirectory(vfsStream::url('exampleDir')); $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('id')); } } ?>
    29. Global State Hidden Global State in PHP  Loaded source files  Loaded classes and functions  Sent HTTP headers  new DateTime()  time()  mktime()  rand() This slide contains material by Miško Hevery
    30. The End Thank you for your interest! These slides will be linked soon from http://sebastian-bergmann.de/ You can vote for this talk on http://joind.in/190
    31. License   This presentation material is published under the Attribution-Share Alike 3.0 Unported license.   You are free: ✔ to Share – to copy, distribute and transmit the work. ✔ to Remix – to adapt the work.   Under the following conditions: ● Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). ● Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.   For any reuse or distribution, you must make clear to others the license terms of this work.   Any of the above conditions can be waived if you get permission from the copyright holder.   Nothing in this license impairs or restricts the author's moral rights.

    + Sebastian BergmannSebastian Bergmann, 5 months ago

    custom

    2928 views, 9 favs, 7 embeds more stats

    How do you write untestable code and anger an ancie more

    More info about this document

    CC Attribution-ShareAlike LicenseCC Attribution-ShareAlike License

    Go to text version

    • Total Views 2928
      • 2421 on SlideShare
      • 507 from embeds
    • Comments 0
    • Favorites 9
    • Downloads 81
    Most viewed embeds
    • 489 views on http://sebastian-bergmann.de
    • 13 views on http://www.planet-php.net
    • 1 views on http://planet-php.org
    • 1 views on http://phpmetablog.com
    • 1 views on http://209.85.195.132

    more

    All embeds
    • 489 views on http://sebastian-bergmann.de
    • 13 views on http://www.planet-php.net
    • 1 views on http://planet-php.org
    • 1 views on http://phpmetablog.com
    • 1 views on http://209.85.195.132
    • 1 views on http://var-dump.com
    • 1 views on http://twiki.corp.yahoo.com

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories

    Groups / Events