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

Testing untestable code - PHPBNL11

on

  • 1,820 views

 

Statistics

Views

Total Views
1,820
Views on SlideShare
1,797
Embed Views
23

Actions

Likes
1
Downloads
18
Comments
0

3 Embeds 23

http://stream.puschkasch.com 11
http://tdpsk.soup.io 7
http://lanyrd.com 5

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 - PHPBNL11 Testing untestable code - PHPBNL11 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
    • About me Stephan Hochdörfer, bitExpert AG Department Manager Research Labs enjoying PHP since 1999 S.Hochdoerfer@bitExpert.de @shochdoerfer
    • Warning! Use at your own risk...
    • No excuse for writing bad code!
    • Seriously, I am not kidding!
    • 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“? s „new“ is evil!
    • 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 Required class class Class to Unittest Class to Unittest Test Test Required Required class class
    • Theory Database Database Required Required class class External Class to External Unittest Class to resource Unittest test resource test Required Required class class Required Required Required Required Webservice class class Webservice class class
    • Theory Database Database Required Required class class External Class to External Unittest Class to resource Unittest test resource test Required Required class class Required Required Required Required Webservice class class 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 Safty instructions Do not change existing code!
    • Testing „untestable“ PHP Code | __autoload<?phpclass Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
    • Testing „untestable“ PHP Code | __autoload<?phpclass Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}How to inject a dependency? Use __autoload
    • Testing „untestable“ PHP Code | __autoload<?phpfunction run_autoload($psClass) { $sFileToInclude = strtolower($psClass)..php; if(strtolower($psClass) == engine) { $sFileToInclude = /custom/mocks/.$sFileToInclude; } include($sFileToInclude);}// Testcasespl_autoload_register(run_autoload);$oCar = new Car(Diesel);echo $oCar->run();
    • Testing „untestable“ PHP Code | include_path<?phpinclude(Engine.php);class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
    • Testing „untestable“ PHP Code | include_path<?phpinclude(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<?phpini_set(include_path, /custom/mocks/.PATH_SEPARATOR. ini_get(include_path));// Testcaseinclude(car.php);$oCar = new Car(Diesel);echo $oCar->run();
    • Testing „untestable“ PHP Code | include_path alternative<?phpinclude(Engine.php);class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
    • Testing „untestable“ PHP Code | include_path alternative<?phpinclude(Engine.php);class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}How to inject a dependency? Custom Stream Wrapper behaviour Idea by Alex Netkachov, http://www.alexatnet.com/node/203
    • Testing „untestable“ PHP Code | include_path alternative<?phpclass 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);
    • Testing „untestable“ PHP Code | include_path alternative<?phpclass 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, AbstractEngine::get, $content); return $content; }}stream_wrapper_unregister(file);stream_wrapper_register(file, CustomFileStreamWrapper);include(engine.php);?>
    • Testing „untestable“ PHP Code How to test private methods?
    • Testing „untestable“ PHP Code How to test private methods? There`s no need to!
    • Testing „untestable“ PHP Code How to test private methods? There`s no need to, but...
    • Testing „untestable“ PHP Code | private vs. protected<?phpclass 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(private function, public function, $content); return $content; }}stream_wrapper_unregister(file);stream_wrapper_register(file, CustomFileStreamWrapper);include(engine.php);?>
    • Testing „untestable“ PHP Code | Namespaces<?phpclass Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); }}
    • Testing „untestable“ PHP Code | Namespaces<?phpclass 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<?phpclass Car { private $Engine; public function __construct($sEngine, $CacheDir) { $this->Engine = CarEngine::getByType($sEngine); mkdir($CacheDir./cache/, 0700, true); }}
    • Testing „untestable“ PHP Code | vfsStream<?phpclass 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 vfsStreamvfsStreamWrapper::register();vfsStreamWrapper::setRoot(new vfsStreamDirectory(app));$oCar = new Car(Diesel, vfsStream::url(app));echo vfsStreamWrapper::getRoot()->hasChild(cache);
    • Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite framework  e.g Zend Framework  Implement Zend_Db_Statement_Interface  subclass Zend_Db_Adapter_Abstract $db = new Custom_Db_Adapter(array()); Zend_Db_Table::setDefaultAdapter($db);
    • Testing „untestable“ PHP Code | Database Database Testing  For low-level database access:  e.g MySQL  do not load the mysql extension  Add custom userland implementations of mysql_* functions
    • Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite unittest framework  e.g PHPUnit  extend PHPUnit_Extensions_Database_TestCase  implement getConnection() and getDataset()
    • Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite sql server (and tools)  e.g MySQL  use MySQL Proxy to transparently switch databases  Begin and rollback transactions
    • 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<?phpfunction 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<?phpfunction 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
    • Testing „untestable“ PHP Code | Test functions<?phpfunction 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<?phpfunction buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail(order@domain.org, New sale, ....);}
    • Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail(order@domain.org, New sale, ....);}
    • Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail(order@domain.org, New sale, ....);}How to test Unfortunatley mail() is part of the PHP core and cannot be unloaded
    • Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction 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<?phpini_set(runkit.internal_override, 1);runkit_function_redefine(mail,,return true;);?>
    • Testing „untestable“ PHP Code
    • Generating testable code What else? Remember: No changes to the source code!
    • Generating testable code What else? Generative Programming
    • Generating testable code Generative Programming Configuration Configuration 1 ... n Implementation Implementation Generator Product components Generator Product components Generator Generator application application
    • Generating testable code Generative Programming Configuration Configuration Application Application Implementation Implementation Generator components Generator components Testcases Testcases Generator Generator application application
    • Generating testable code Generative Programming A frame is a data structure for representing knowledge.
    • Generating testable codeFileFrm 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()<?phpfunction buyCar(Car $oCar) {global $oDB;<!{Prefix}!>mysql_query(„INSERT INTO...“, $oDB);<!{MailSlot}!>}?>ENDCONTENT() }}
    • Generating testable codeFileFrm 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()<?phpfunction buyCar(Car $oCar) {global $oDB;<!{Prefix}!>mysql_query(„INSERT INTO...“, $oDB);<!{MailSlot}!>}?>ENDCONTENT() }}
    • Generating testable code Generative Programming Extraction  Show / hide parts of the code Example MailSlot: mail(order@domain.org, New sale, ....);
    • Generating testable code Generative Programming Extraction  Show / hide parts of the code 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, ....); } ?>
    • Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, methods, classes Example Prefix: test_
    • Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, methods, classes Example Prefix: test_ <?php function buyCar(Car $oCar) { global $oDB; test_mysql_query("INSERT INTO...", $oDB); }
    • Conclusion How much effort to take?
    • Conclusion Conclusion Change your mindset to write testable code!
    • Conclusion Conclusion PHP is a swiss army knife.
    • http://joind.in/2420