• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Testing untestable code - DPC10
 

Testing untestable code - DPC10

on

  • 2,669 views

 

Statistics

Views

Total Views
2,669
Views on SlideShare
2,665
Embed Views
4

Actions

Likes
3
Downloads
28
Comments
0

2 Embeds 4

http://www.slideshare.net 2
http://lanyrd.com 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Testing untestable code - DPC10 Testing untestable code - DPC10 Presentation Transcript

    • Testing untestable Code Stephan Hochdörfer, bitExpert AG "Quality is a function of thought and reflection - precise thought and reflection. That’s the magic." Michael Feathers
    • Agenda 1. About me 2. Theory 3. How to test untestable code 4. Generating testable code 5. Conclusions 6. Questions
    • About me  Stephan Hochdörfer, bitExpert AG  Department Manager Research Labs  enjoying PHP since 1999  S.Hochdoerfer@bitExpert.de  @shochdoerfer
    • No excuse for bad code!
    • Warning! Use at own risk...
    • Theory "There is no secret to writing tests, there are only secrets to write testable code!" Miško Hevery
    • Theory What is „untestable code“?
    • Theory What is „untestable code“?
    • Theory What is „untestable code“?
    • Theory "...our test strategy requires us to have more control or visibility of the internal behavior of the system under test." Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code
    • Theory Required class Class to Unittest Test Required class
    • Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
    • Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
    • Theory How to achieve „testable“ code?
    • Theory How to achieve „testable“ code? Refactoring
    • Theory "Before you start refactoring, check that you have a solid suite of tests." Martin Fowler, Refactoring
    • Testing „untestable“ PHP Code Let the work begin...
    • Testing „untestable“ PHP Code For your safty! Do not change existing code!
    • Testing „untestable“ PHP Code | __autoload <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
    • Testing „untestable“ PHP Code | __autoload <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } } How to inject a dependency?  Use __autoload
    • Testing „untestable“ PHP Code | __autoload <?php function run_autoload($psClass) { $sFileToInclude = strtolower($psClass).'.php'; if(strtolower($psClass) == 'engine') { $sFileToInclude = '/custom/mocks/'.$sFileToInclude; } include($sFileToInclude); } // Testcase spl_autoload_register('run_autoload'); $oCar = new Car('Diesel'); echo $oCar->run();
    • Testing „untestable“ PHP Code | include_path <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
    • Testing „untestable“ PHP Code | include_path <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } } How to inject a dependency?  Manipulate include_path setting
    • Testing „untestable“ PHP Code | include_path <?php ini_set('include_path', '/custom/mocks/'.PATH_SEPARATOR. ini_get('include_path')); // Testcase include('car.php'); $oCar = new Car('Diesel'); echo $oCar->run();
    • Testing „untestable“ PHP Code | include_path alternative <?php class CustomFileStreamWrapper { private $_handler; function stream_open($path, $mode, $options, &$opened_path) { stream_wrapper_restore('file'); // @TODO: modify $path before fopen $this->_handler = fopen($path, $mode); stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); return true; } function stream_read($count) {} function stream_write($data) {} function stream_tell() {} function stream_eof() {} function stream_seek($offset, $whence) {} } stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); Source: Alex Netkachov, http://www.alexatnet.com/node/203
    • Testing „untestable“ PHP Code | include_path alternative <?php class CustomFileStreamWrapper { private $_handler; function stream_open($path, $mode, $options, &$opened_path) { stream_wrapper_restore('file'); $this->_handler = fopen($path, $mode); stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); return true; } function stream_read($count) { $content = fread($this->_handler, $count); $content = str_replace('Engine::getByType', 'MockedEngine::get', $content); return $content; } } stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); include('engine.php'); ?>
    • Testing „untestable“ PHP Code | Namespaces <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); } }
    • Testing „untestable“ PHP Code | Namespaces <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); } } How to inject a dependency?  Use __autoload or manipulate the include_path
    • Testing „untestable“ PHP Code | vfsStream <?php class Car { private $Engine; public function __construct($sEngine, $CacheDir) { $this->Engine = CarEngine::getByType($sEngine); mkdir($CacheDir.'/cache/', 0700, true); } }
    • Testing „untestable“ PHP Code | vfsStream <?php class Car { private $Engine; public function __construct($sEngine, $CacheDir) { $this->Engine = CarEngine::getByType($sEngine); mkdir($CacheDir.'/cache/', 0700, true); } } How mock a filesystem?  Use vfsStream - http://code.google.com/p/bovigo/
    • Testing „untestable“ PHP Code | vfsStream <?php // setup vfsStream vfsStreamWrapper::register(); vfsStreamWrapper::setRoot(new vfsStreamDirectory('app')); $oCar = new Car('Diesel', vfsStream::url('app')); echo vfsStreamWrapper::getRoot()->hasChild('cache');
    • Testing „untestable“ PHP Code „I have no idea how to unit-test procedural code. Unit-testing assumes that I can instantiate a piece of my application in isolation.“ Miško Hevery
    • Testing „untestable“ PHP Code | Test functions <?php function startsWith($sString, $psPre) { return $psPre == substr($sString, 0, strlen($psPre)); } function contains($sString, $sSearch) { return false !== strpos($sString, $sSearch); }
    • Testing „untestable“ PHP Code | Test functions <?php function startsWith($sString, $psPre) { return $psPre == substr($sString, 0, strlen($psPre)); } function contains($sString, $sSearch) { return false !== strpos($sString, $sSearch); } How to test  PHPUnit can call functions  PHPUnit can save/restore globale state
    • Testing „untestable“ PHP Code | overwrite internal functions <?php function buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail('order@domain.org', 'New sale', '....'); }
    • Testing „untestable“ PHP Code | overwrite internal functions <?php function buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail('order@domain.org', 'New sale', '....'); } How to test  Do not load mysql extension. Provide own implementation  Unfortunatley mail() is part of the PHP core
    • Testing „untestable“ PHP Code | overwrite internal functions <?php function buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail('order@domain.org', 'New sale', '....'); } How to test  Use classkit extension to overwrite internal functions
    • Testing „untestable“ PHP Code | overwrite internal functions <?php ini_set('runkit.internal_override', '1'); runkit_function_redefine('mail','','return true;'); ?>
    • Testing „untestable“ PHP Code
    • Generating testable code Generative Programming
    • Generating testable code Generative Programming Configuration 1 ... n Implementation Generator Product components Generator application
    • Generating testable code Generative Programming Configuration Application Implementation Generator components Testcases Generator application
    • Generating testable code Course of action Extraction  „Mask“ parts of the code Customizing  Change content of global vars  Pre/Postfixes for own functions, methods, classes Recombine  Re-order parts of the code
    • Generating testable code FileFrm FILEIndex_php5 { private String Prefix = "test_"; private String MailSlot = "mail('order@domain.org', 'New sale', '....');"; public FILEIndex_php5() { setFilename("index.php5"); setRelativePath("/"); } private void assign() { BEGINCONTENT() <?php function buyCar(Car $oCar) { global $oDB; <!{Prefix}!>mysql_query(„INSERT INTO...“, $oDB); <!{MailSlot}!> } ?> ENDCONTENT() } }
    • Generating testable code 1. Example Prefix: test_ <?php function buyCar(Car $oCar) { global $oDB; test_mysql_query("INSERT INTO...", $oDB); }
    • Generating testable code 1. Example Prefix: test_ <?php function buyCar(Car $oCar) { global $oDB; test_mysql_query("INSERT INTO...", $oDB); } 2. Example MailSlot: mail('order@domain.org', 'New sale', '....'); <?php function buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail('order@domain.org', 'New sale', '....'); } ?>
    • Conclusion How much effort to take?
    • Conclusion Conclusion  Change mindset to write testable code  Dependency Injection  Look for other options to raise the bar  Work around limitations of PHP  PHP is flexible, use it that way
    • http://joind.in/1545