0
Testing untestable code   Stephan Hochdörfer, bitExpert AG "Quality is a function of thought and reflection - precise thou...
About me Stephan Hochdörfer, bitExpert AG Department Manager Research Labs enjoying PHP since 1999 S.Hochdoerfer@bitEx...
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!"              ...
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 t...
Theory                                 Required                                  Required                                 ...
Theory                                             Database                                              Database         ...
Theory                                             Database                                              Database         ...
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."               ...
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) {...
Testing „untestable“ PHP Code | __autoload<?phpclass Car {    private $Engine;     public function __construct($sEngine) {...
Testing „untestable“ PHP Code | __autoload<?phpfunction run_autoload($psClass) {    $sFileToInclude = strtolower($psClass)...
Testing „untestable“ PHP Code | include_path<?phpinclude(Engine.php);class Car {    private $Engine;     public function _...
Testing „untestable“ PHP Code | include_path<?phpinclude(Engine.php);class Car {    private $Engine;     public function _...
Testing „untestable“ PHP Code | include_path<?phpini_set(include_path,    /custom/mocks/.PATH_SEPARATOR.    ini_get(includ...
Testing „untestable“ PHP Code | include_path alternative<?phpinclude(Engine.php);class Car {    private $Engine;     publi...
Testing „untestable“ PHP Code | include_path alternative<?phpinclude(Engine.php);class Car {    private $Engine;     publi...
Testing „untestable“ PHP Code | include_path alternative<?phpclass CustomFileStreamWrapper {  private $_handler;    functi...
Testing „untestable“ PHP Code | include_path alternative<?phpclass CustomFileStreamWrapper {    private $_handler;     fun...
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;     functi...
Testing „untestable“ PHP Code | Namespaces<?phpclass Car {    private $Engine;     public function __construct($sEngine) {...
Testing „untestable“ PHP Code | Namespaces<?phpclass Car {    private $Engine;     public function __construct($sEngine) {...
Testing „untestable“ PHP Code | vfsStream<?phpclass Car {    private $Engine;     public function __construct($sEngine, $C...
Testing „untestable“ PHP Code | vfsStream<?phpclass Car {    private $Engine;     public function __construct($sEngine, $C...
Testing „untestable“ PHP Code | vfsStream<?php// setup vfsStreamvfsStreamWrapper::register();vfsStreamWrapper::setRoot(new...
Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite framework  e.g Zen...
Testing „untestable“ PHP Code | Database Database Testing  For low-level database access:  e.g MySQL              do no...
Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite unittest framework ...
Testing „untestable“ PHP Code | Database Database Testing  Use the methods provided by your favourite sql server (and too...
Testing „untestable“ PHP Code       „I have no idea how to unit-test procedural code. Unit-testing          assumes that I...
Testing „untestable“ PHP Code | Test functions<?phpfunction startsWith($sString, $psPre) {    return $psPre == substr($sSt...
Testing „untestable“ PHP Code | Test functions<?phpfunction startsWith($sString, $psPre) {    return $psPre == substr($sSt...
Testing „untestable“ PHP Code | Test functions<?phpfunction startsWith($sString, $psPre) {    return $psPre == substr($sSt...
Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) {    global $oDB;     mysql_qu...
Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) {    global $oDB;     mysql_qu...
Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) {    global $oDB;     mysql_qu...
Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) {    global $oDB;     mysql_qu...
Testing „untestable“ PHP Code | overwrite internal functions<?phpini_set(runkit.internal_override, 1);runkit_function_rede...
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                            Configu...
Generating testable code Generative Programming                           Configuration                            Configu...
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(orde...
Generating testable codeFileFrm FILEIndex_php5 {  private String Prefix   = "test_";  private String MailSlot = "mail(orde...
Generating testable code Generative Programming Extraction  Show / hide parts of the code Example MailSlot: mail(order@do...
Generating testable code Generative Programming Extraction  Show / hide parts of the code Example MailSlot: mail(order@do...
Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, m...
Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, m...
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
Upcoming SlideShare
Loading in...5
×

Testing untestable code - PHPBNL11

1,608

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,608
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
19
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Testing untestable code - PHPBNL11"

  1. 1. 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
  2. 2. About me Stephan Hochdörfer, bitExpert AG Department Manager Research Labs enjoying PHP since 1999 S.Hochdoerfer@bitExpert.de @shochdoerfer
  3. 3. Warning! Use at your own risk...
  4. 4. No excuse for writing bad code!
  5. 5. Seriously, I am not kidding!
  6. 6. Theory "There is no secret to writing tests, there are only secrets to write testable code!" Miško Hevery
  7. 7. Theory What is „untestable code“?
  8. 8. Theory What is „untestable code“? s „new“ is evil!
  9. 9. Theory What is „untestable code“?
  10. 10. Theory What is „untestable code“?
  11. 11. 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
  12. 12. Theory Required Required class class Class to Unittest Class to Unittest Test Test Required Required class class
  13. 13. 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
  14. 14. 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
  15. 15. Theory How to achieve „testable“ code?
  16. 16. Theory How to achieve „testable“ code? Refactoring
  17. 17. Theory "Before you start refactoring, check that you have a solid suite of tests." Martin Fowler, Refactoring
  18. 18. Testing „untestable“ PHP Code Let the work begin...
  19. 19. Testing „untestable“ PHP Code Safty instructions Do not change existing code!
  20. 20. Testing „untestable“ PHP Code | __autoload<?phpclass Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
  21. 21. 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
  22. 22. 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();
  23. 23. Testing „untestable“ PHP Code | include_path<?phpinclude(Engine.php);class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
  24. 24. 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
  25. 25. 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();
  26. 26. Testing „untestable“ PHP Code | include_path alternative<?phpinclude(Engine.php);class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); }}
  27. 27. 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
  28. 28. 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);
  29. 29. 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);?>
  30. 30. Testing „untestable“ PHP Code How to test private methods?
  31. 31. Testing „untestable“ PHP Code How to test private methods? There`s no need to!
  32. 32. Testing „untestable“ PHP Code How to test private methods? There`s no need to, but...
  33. 33. 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);?>
  34. 34. Testing „untestable“ PHP Code | Namespaces<?phpclass Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); }}
  35. 35. 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
  36. 36. Testing „untestable“ PHP Code | vfsStream<?phpclass Car { private $Engine; public function __construct($sEngine, $CacheDir) { $this->Engine = CarEngine::getByType($sEngine); mkdir($CacheDir./cache/, 0700, true); }}
  37. 37. 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/
  38. 38. 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);
  39. 39. 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);
  40. 40. 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
  41. 41. 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()
  42. 42. 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
  43. 43. 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
  44. 44. 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);}
  45. 45. 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
  46. 46. 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
  47. 47. Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail(order@domain.org, New sale, ....);}
  48. 48. Testing „untestable“ PHP Code | overwrite internal functions<?phpfunction buyCar(Car $oCar) { global $oDB; mysql_query("INSERT INTO...", $oDB); mail(order@domain.org, New sale, ....);}
  49. 49. 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
  50. 50. 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
  51. 51. Testing „untestable“ PHP Code | overwrite internal functions<?phpini_set(runkit.internal_override, 1);runkit_function_redefine(mail,,return true;);?>
  52. 52. Testing „untestable“ PHP Code
  53. 53. Generating testable code What else? Remember: No changes to the source code!
  54. 54. Generating testable code What else? Generative Programming
  55. 55. Generating testable code Generative Programming Configuration Configuration 1 ... n Implementation Implementation Generator Product components Generator Product components Generator Generator application application
  56. 56. Generating testable code Generative Programming Configuration Configuration Application Application Implementation Implementation Generator components Generator components Testcases Testcases Generator Generator application application
  57. 57. Generating testable code Generative Programming A frame is a data structure for representing knowledge.
  58. 58. 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() }}
  59. 59. 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() }}
  60. 60. Generating testable code Generative Programming Extraction  Show / hide parts of the code Example MailSlot: mail(order@domain.org, New sale, ....);
  61. 61. 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, ....); } ?>
  62. 62. Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, methods, classes Example Prefix: test_
  63. 63. 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); }
  64. 64. Conclusion How much effort to take?
  65. 65. Conclusion Conclusion Change your mindset to write testable code!
  66. 66. Conclusion Conclusion PHP is a swiss army knife.
  67. 67. http://joind.in/2420
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×