Your SlideShare is downloading. ×
0
Testing untestable Code
   Stephan Hochdörfer, bitExpert AG




 "Quality is a function of thought and reflection -
 preci...
Agenda
1.   About me

2.   Theory

3.   How to test untestable code

4.   Generating testable code

5.   Conclusions

6.  ...
About me
 Stephan Hochdörfer, bitExpert AG

 Department Manager Research Labs

 enjoying PHP since 1999

 S.Hochdoerfe...
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!"
      ...
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 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."
        ...
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($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 | 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...
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...
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_...
Testing „untestable“ PHP Code
Generating testable code



 Generative Programming
Generating testable code



 Generative Programming

                             Configuration




                      ...
Generating testable code



 Generative Programming

                             Configuration


                        ...
Generating testable code



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


 Customizing
  Change content of ...
Generating testable code




FileFrm FILEIndex_php5 {
  private String Prefix   = "test_";
  private String MailSlot = "ma...
Generating testable code




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

    test_mysql_qu...
Generating testable code




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

     test_mysql_q...
Conclusion



 How much effort to take?
Conclusion



 Conclusion

  Change mindset to write testable code
        
          Dependency Injection

  Look for ...
http://joind.in/1545
Upcoming SlideShare
Loading in...5
×

Testing untestable code - DPC10

2,097

Published on

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

No Downloads
Views
Total Views
2,097
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
29
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Transcript of "Testing untestable code - DPC10"

  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. Agenda 1. About me 2. Theory 3. How to test untestable code 4. Generating testable code 5. Conclusions 6. Questions
  3. 3. About me  Stephan Hochdörfer, bitExpert AG  Department Manager Research Labs  enjoying PHP since 1999  S.Hochdoerfer@bitExpert.de  @shochdoerfer
  4. 4. No excuse for bad code!
  5. 5. Warning! Use at own risk...
  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“?
  9. 9. Theory What is „untestable code“?
  10. 10. 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
  11. 11. Theory Required class Class to Unittest Test Required class
  12. 12. Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
  13. 13. Theory Database Required class External Class to Unittest resource test Required class Required Required Webservice class class
  14. 14. Theory How to achieve „testable“ code?
  15. 15. Theory How to achieve „testable“ code? Refactoring
  16. 16. Theory "Before you start refactoring, check that you have a solid suite of tests." Martin Fowler, Refactoring
  17. 17. Testing „untestable“ PHP Code Let the work begin...
  18. 18. Testing „untestable“ PHP Code For your safty! Do not change existing code!
  19. 19. Testing „untestable“ PHP Code | __autoload <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  20. 20. 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
  21. 21. 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();
  22. 22. Testing „untestable“ PHP Code | include_path <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  23. 23. 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
  24. 24. 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();
  25. 25. 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
  26. 26. 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'); ?>
  27. 27. Testing „untestable“ PHP Code | Namespaces <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); } }
  28. 28. 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
  29. 29. 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); } }
  30. 30. 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/
  31. 31. 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');
  32. 32. 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
  33. 33. 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); }
  34. 34. 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
  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', '....'); }
  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  Do not load mysql extension. Provide own implementation  Unfortunatley mail() is part of the PHP core
  37. 37. 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
  38. 38. Testing „untestable“ PHP Code | overwrite internal functions <?php ini_set('runkit.internal_override', '1'); runkit_function_redefine('mail','','return true;'); ?>
  39. 39. Testing „untestable“ PHP Code
  40. 40. Generating testable code Generative Programming
  41. 41. Generating testable code Generative Programming Configuration 1 ... n Implementation Generator Product components Generator application
  42. 42. Generating testable code Generative Programming Configuration Application Implementation Generator components Testcases Generator application
  43. 43. 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
  44. 44. 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() } }
  45. 45. Generating testable code 1. Example Prefix: test_ <?php function buyCar(Car $oCar) { global $oDB; test_mysql_query("INSERT INTO...", $oDB); }
  46. 46. 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', '....'); } ?>
  47. 47. Conclusion How much effort to take?
  48. 48. 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
  49. 49. http://joind.in/1545
  1. A particular slide catching your eye?

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

×