Testing untestable Code
   Stephan Hochdörfer, bitExpert AG




 "Quality is a function of thought and reflection -
 preci...
About me
 Founder of bitExpert AG, Mannheim

 Field of duty
         
           Department Manager of Research Labs
  ...
Agenda
1.   Theory

2.   How to test untestable PHP Code

3.   Generate testable code

4.   Conclusion
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“?

  Global variables
          
            Singleton
          
            Regis...
Theory




          "...our test strategy requires us to have more control or
         visibility of the internal behavio...
Theory




                               Required
                                class
                    Class to
    ...
Theory




                                          Database

                               Required
                   ...
Theory




                                          Database

                               Required
                   ...
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."
        ...
Agenda
1.   Theory

2.   Testing untestable PHP Code

3.   Generate testable code

4.   Conclusion
Agenda
1.   Theory

2.   Testing untestable PHP Code
     – Hardcoded Dependencies
     – Procedural code
     – Overwrite...
Testing „untestable“ PHP Code



 Assumption




          Do not change existing code!
Testing „untestable“ PHP Code | __autoload




<?php
class Car {
    private $Engine;

     public function __construct($s...
Testing „untestable“ PHP Code | __autoload




<?php
class Car {
    private $Engine;

     public function __construct($s...
Testing „untestable“ PHP Code | __autoload




<?php
function run_autoload($psClass) {
    $sFileToInclude = strtolower($p...
Testing „untestable“ PHP Code | include_path




<?php
include('Engine.php');

class Car {
    private $Engine;

     publ...
Testing „untestable“ PHP Code | include_path




<?php
include('Engine.php');

class Car {
    private $Engine;

     publ...
Testing „untestable“ PHP Code | include_path




<?php
ini_set('include_path',
    '/custom/mocks/'.PATH_SEPARATOR.
    in...
Testing „untestable“ PHP Code | include_path alternative

<?php
class CustomFileStreamWrapper {
  private $_handler;

    ...
Testing „untestable“ PHP Code | Namespaces




<?php
class Car {
    private $Engine;

     public function __construct($s...
Testing „untestable“ PHP Code | Namespaces




<?php
class Car {
    private $Engine;

     public function __construct($s...
Testing „untestable“ PHP Code | vfsStream




<?php
class Car {
    private $Engine;

     public function __construct($sE...
Testing „untestable“ PHP Code | vfsStream




<?php
class Car {
    private $Engine;

     public function __construct($sE...
Testing „untestable“ PHP Code | vfsStream




<?php

// setup vfsStream
vfsStreamWrapper::register();
vfsStreamWrapper::se...
Agenda
1.   Theory

2.   Testing untestable PHP Code
     – Hardcoded Dependencies
     – Procedural code
     – Overwrite...
Testing „untestable“ PHP Code




       „I have no idea how to unit-test procedural code. Unit-testing
          assumes ...
Testing „untestable“ PHP Code | Test functions




<?php
function startsWith($sString, $psPre) {
    return $psPre == subs...
Testing „untestable“ PHP Code | Test functions




<?php
function startsWith($sString, $psPre) {
    return $psPre == subs...
Agenda
1.   Theory

2.   Testing untestable PHP Code
     – Hardcoded Dependencies
     – Procedural code
     – Overwrite...
Testing „untestable“ PHP Code | overwrite internal functions




<?php
function buyCar(Car $oCar) {
    global $oDB;

    ...
Testing „untestable“ PHP Code | overwrite internal functions




<?php
function buyCar(Car $oCar) {
    global $oDB;

    ...
Testing „untestable“ PHP Code | overwrite internal functions




<?php
function buyCar(Car $oCar) {
    global $oDB;

    ...
Testing „untestable“ PHP Code | overwrite internal functions




<?php

ini_set('runkit.internal_override', '1');

runkit_...
Agenda
1.   Theory

2.   Testing untestable PHP Code

3.   Generate testable code

4.   Conclusion
Generate testable code



 Generative Programming

                             Configuration




                        ...
Generate testable code



 Generative Programming

                             Configuration


                          ...
Generate testable code



 Course of action
 Extraction
  „Mask“ parts of the code


 Customizing
  Change content of gl...
Generate testable code




FileFrm FILEIndex_php5 {
  private String Prefix   = "test_";
  private String MailSlot = "mail...
Generate testable code




1. Example
Prefix: test_
<?php
function buyCar(Car $oCar) {
  global $oDB;

    test_mysql_quer...
Generate testable code




1. Example
Prefix: test_
<?php
function buyCar(Car $oCar) {
  global $oDB;

     test_mysql_que...
Conclustion



 Conclusion

  Change mindset to write testable code
        
          Dependency Injection

  Look for...
Testing untestable Code - PFCongres 2010
Upcoming SlideShare
Loading in …5
×

Testing untestable Code - PFCongres 2010

2,571 views
2,439 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,571
On SlideShare
0
From Embeds
0
Number of Embeds
21
Actions
Shares
0
Downloads
8
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Testing untestable Code - PFCongres 2010

  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  Founder of bitExpert AG, Mannheim  Field of duty  Department Manager of Research Labs  Head of Development for bitFramework  Focusing on  PHP  Generative Programming  Contact me  @shochdoerfer  S.Hochdoerfer@bitExpert.de
  3. 3. Agenda 1. Theory 2. How to test untestable PHP Code 3. Generate testable code 4. Conclusion
  4. 4. Theory "There is no secret to writing tests, there are only secrets to write testable code!" Miško Hevery
  5. 5. Theory What is „untestable code“?
  6. 6. Theory What is „untestable code“?  Global variables  Singleton  Registry  Globale state  static method calls  Control flow in a constructor  Tight coupling  Dependencies to concrete implementations  to many dependencies to other classes  Dependencies without control
  7. 7. 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
  8. 8. Theory Required class Class to Unittest Test Required class
  9. 9. Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
  10. 10. Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
  11. 11. Theory How to achieve „testable“ code?
  12. 12. Theory How to achieve „testable“ code? Refactoring
  13. 13. Theory "Before you start refactoring, check that you have a solid suite of tests." Martin Fowler, Refactoring
  14. 14. Agenda 1. Theory 2. Testing untestable PHP Code 3. Generate testable code 4. Conclusion
  15. 15. Agenda 1. Theory 2. Testing untestable PHP Code – Hardcoded Dependencies – Procedural code – Overwrite internal PHP functions 3. Generate testable code 4. Conclusion
  16. 16. Testing „untestable“ PHP Code Assumption Do not change existing code!
  17. 17. Testing „untestable“ PHP Code | __autoload <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  18. 18. 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
  19. 19. 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();
  20. 20. Testing „untestable“ PHP Code | include_path <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  21. 21. 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
  22. 22. 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();
  23. 23. 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
  24. 24. Testing „untestable“ PHP Code | Namespaces <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); } }
  25. 25. 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
  26. 26. 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); } }
  27. 27. 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/
  28. 28. 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');
  29. 29. Agenda 1. Theory 2. Testing untestable PHP Code – Hardcoded Dependencies – Procedural code – Overwrite internal PHP functions 3. Generate testable code 4. Conclusion
  30. 30. 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
  31. 31. 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); }
  32. 32. 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 methods  PHPUnit can save/restore globale state
  33. 33. Agenda 1. Theory 2. Testing untestable PHP Code – Hardcoded Dependencies – Procedural code – Overwrite internal PHP functions 3. Generate testable code 4. Conclusion
  34. 34. 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', '....'); }
  35. 35. 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  Unfortunatly mail() is part of the PHP core
  36. 36. 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 mock internal functions
  37. 37. Testing „untestable“ PHP Code | overwrite internal functions <?php ini_set('runkit.internal_override', '1'); runkit_function_redefine('mail','','return true;'); ?>
  38. 38. Agenda 1. Theory 2. Testing untestable PHP Code 3. Generate testable code 4. Conclusion
  39. 39. Generate testable code Generative Programming Configuration 1 ... n Implementation Generator Product components Generator application
  40. 40. Generate testable code Generative Programming Configuration Application Implementation Generator components Testcases Generator application
  41. 41. Generate 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
  42. 42. Generate 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() } }
  43. 43. Generate testable code 1. Example Prefix: test_ <?php function buyCar(Car $oCar) { global $oDB; test_mysql_query("INSERT INTO...", $oDB); }
  44. 44. Generate 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', '....'); } ?>
  45. 45. Conclustion 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

×