SlideShare a Scribd company logo
1 of 67
Download to read offline
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




<?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
include('Engine.php');

class Car {
    private $Engine;

     public function __construct($sEngine) {
         $this->Engine = Engine::getByType($sEngine);
     }
}
Testing „untestable“ PHP Code | include_path alternative




<?php
include('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

<?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');
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', '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

<?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('private function', 'public function',
              $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 | 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




<?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
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', '....');
}
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
 Unfortunatley mail() is part of the PHP core and cannot be unloaded
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


 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 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




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


 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

More Related Content

What's hot

Java Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedJava Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedyearninginjava
 
Living With Legacy Code
Living With Legacy CodeLiving With Legacy Code
Living With Legacy CodeRowan Merewood
 
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,..."Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...Vladimir Ivanov
 
Java Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner WalkthroughJava Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner WalkthroughMahfuz Islam Bhuiyan
 
Getting started with the JNI
Getting started with the JNIGetting started with the JNI
Getting started with the JNIKirill Kounik
 
Dev labs alliance top 20 basic java interview question for sdet
Dev labs alliance top 20 basic java interview question for sdetDev labs alliance top 20 basic java interview question for sdet
Dev labs alliance top 20 basic java interview question for sdetdevlabsalliance
 
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016Christian Schneider
 
Mastering Mock Objects - Advanced Unit Testing for Java
Mastering Mock Objects - Advanced Unit Testing for JavaMastering Mock Objects - Advanced Unit Testing for Java
Mastering Mock Objects - Advanced Unit Testing for JavaDenilson Nastacio
 
Cracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 ExamsCracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 ExamsGanesh Samarthyam
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
OCP Java (OCPJP) 8 Exam Quick Reference Card
OCP Java (OCPJP) 8 Exam Quick Reference CardOCP Java (OCPJP) 8 Exam Quick Reference Card
OCP Java (OCPJP) 8 Exam Quick Reference CardHari kiran G
 
Java object oriented programming - OOPS
Java object oriented programming - OOPSJava object oriented programming - OOPS
Java object oriented programming - OOPSrithustutorials
 
Java tutorials
Java tutorialsJava tutorials
Java tutorialssaryu2011
 

What's hot (20)

Viva file
Viva fileViva file
Viva file
 
Java Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedJava Faqs useful for freshers and experienced
Java Faqs useful for freshers and experienced
 
Java reflection
Java reflectionJava reflection
Java reflection
 
Living With Legacy Code
Living With Legacy CodeLiving With Legacy Code
Living With Legacy Code
 
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,..."Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...
"Formal Verification in Java" by Shura Iline, Vladimir Ivanov @ JEEConf 2013,...
 
Java Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner WalkthroughJava Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner Walkthrough
 
Getting started with the JNI
Getting started with the JNIGetting started with the JNI
Getting started with the JNI
 
Dev labs alliance top 20 basic java interview question for sdet
Dev labs alliance top 20 basic java interview question for sdetDev labs alliance top 20 basic java interview question for sdet
Dev labs alliance top 20 basic java interview question for sdet
 
Java Reflection @KonaTechAdda
Java Reflection @KonaTechAddaJava Reflection @KonaTechAdda
Java Reflection @KonaTechAdda
 
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
 
Java SE 8 best practices
Java SE 8 best practicesJava SE 8 best practices
Java SE 8 best practices
 
Mastering Mock Objects - Advanced Unit Testing for Java
Mastering Mock Objects - Advanced Unit Testing for JavaMastering Mock Objects - Advanced Unit Testing for Java
Mastering Mock Objects - Advanced Unit Testing for Java
 
Java Tutorial
Java TutorialJava Tutorial
Java Tutorial
 
Cracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 ExamsCracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 Exams
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
OCP Java (OCPJP) 8 Exam Quick Reference Card
OCP Java (OCPJP) 8 Exam Quick Reference CardOCP Java (OCPJP) 8 Exam Quick Reference Card
OCP Java (OCPJP) 8 Exam Quick Reference Card
 
Java object oriented programming - OOPS
Java object oriented programming - OOPSJava object oriented programming - OOPS
Java object oriented programming - OOPS
 
Exception handling
Exception handlingException handling
Exception handling
 
Java tutorials
Java tutorialsJava tutorials
Java tutorials
 
02 basic java programming and operators
02 basic java programming and operators02 basic java programming and operators
02 basic java programming and operators
 

Similar to Testing untestable PHP code

Testing untestable Code - PFCongres 2010
Testing untestable Code - PFCongres 2010Testing untestable Code - PFCongres 2010
Testing untestable Code - PFCongres 2010Stephan Hochdörfer
 
Developer testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticDeveloper testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticLB Denker
 
Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022Mark Niebergall
 
Real world dependency injection - DPC10
Real world dependency injection - DPC10Real world dependency injection - DPC10
Real world dependency injection - DPC10Stephan Hochdörfer
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?Nikita Popov
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Fwdays
 
Testing untestable code - phpday
Testing untestable code - phpdayTesting untestable code - phpday
Testing untestable code - phpdayStephan Hochdörfer
 
Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023Mark Niebergall
 
PCAP Certification Mini Flashcards by MyExamCloud
PCAP Certification Mini Flashcards by MyExamCloudPCAP Certification Mini Flashcards by MyExamCloud
PCAP Certification Mini Flashcards by MyExamCloudGanesh P
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2Yi-Huan Chan
 
Real World Dependency Injection - PFCongres 2010
Real World Dependency Injection - PFCongres 2010Real World Dependency Injection - PFCongres 2010
Real World Dependency Injection - PFCongres 2010Stephan Hochdörfer
 
PHP - Introduction to Object Oriented Programming with PHP
PHP -  Introduction to  Object Oriented Programming with PHPPHP -  Introduction to  Object Oriented Programming with PHP
PHP - Introduction to Object Oriented Programming with PHPVibrant Technologies & Computers
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1Yi-Huan Chan
 
Invoke dynamite in Java EE with invoke dynamic
Invoke dynamite in Java EE with invoke dynamicInvoke dynamite in Java EE with invoke dynamic
Invoke dynamite in Java EE with invoke dynamicAntoine Sabot-Durand
 
Developer testing 201: When to Mock and When to Integrate
Developer testing 201: When to Mock and When to IntegrateDeveloper testing 201: When to Mock and When to Integrate
Developer testing 201: When to Mock and When to IntegrateLB Denker
 
Unit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentUnit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentMark Niebergall
 
Better Testing With PHP Unit
Better Testing With PHP UnitBetter Testing With PHP Unit
Better Testing With PHP Unitsitecrafting
 
Framework prototype
Framework prototypeFramework prototype
Framework prototypeDevMix
 
Framework prototype
Framework prototypeFramework prototype
Framework prototypeDevMix
 

Similar to Testing untestable PHP code (20)

Testing untestable Code - PFCongres 2010
Testing untestable Code - PFCongres 2010Testing untestable Code - PFCongres 2010
Testing untestable Code - PFCongres 2010
 
Developer testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticDeveloper testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing Fanatic
 
Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022Leveling Up With Unit Testing - LonghornPHP 2022
Leveling Up With Unit Testing - LonghornPHP 2022
 
Real world dependency injection - DPC10
Real world dependency injection - DPC10Real world dependency injection - DPC10
Real world dependency injection - DPC10
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"
 
Testing untestable code - phpday
Testing untestable code - phpdayTesting untestable code - phpday
Testing untestable code - phpday
 
Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023Leveling Up With Unit Testing - php[tek] 2023
Leveling Up With Unit Testing - php[tek] 2023
 
PCAP Certification Mini Flashcards by MyExamCloud
PCAP Certification Mini Flashcards by MyExamCloudPCAP Certification Mini Flashcards by MyExamCloud
PCAP Certification Mini Flashcards by MyExamCloud
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2
 
Real World Dependency Injection - PFCongres 2010
Real World Dependency Injection - PFCongres 2010Real World Dependency Injection - PFCongres 2010
Real World Dependency Injection - PFCongres 2010
 
PHP - Introduction to Object Oriented Programming with PHP
PHP -  Introduction to  Object Oriented Programming with PHPPHP -  Introduction to  Object Oriented Programming with PHP
PHP - Introduction to Object Oriented Programming with PHP
 
Test
TestTest
Test
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1
 
Invoke dynamite in Java EE with invoke dynamic
Invoke dynamite in Java EE with invoke dynamicInvoke dynamite in Java EE with invoke dynamic
Invoke dynamite in Java EE with invoke dynamic
 
Developer testing 201: When to Mock and When to Integrate
Developer testing 201: When to Mock and When to IntegrateDeveloper testing 201: When to Mock and When to Integrate
Developer testing 201: When to Mock and When to Integrate
 
Unit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentUnit Testing from Setup to Deployment
Unit Testing from Setup to Deployment
 
Better Testing With PHP Unit
Better Testing With PHP UnitBetter Testing With PHP Unit
Better Testing With PHP Unit
 
Framework prototype
Framework prototypeFramework prototype
Framework prototype
 
Framework prototype
Framework prototypeFramework prototype
Framework prototype
 

More from Stephan Hochdörfer

Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Stephan Hochdörfer
 
Phing for power users - frOSCon8
Phing for power users - frOSCon8Phing for power users - frOSCon8
Phing for power users - frOSCon8Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Stephan Hochdörfer
 
Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Stephan Hochdörfer
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Stephan Hochdörfer
 
Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Stephan Hochdörfer
 
Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Stephan Hochdörfer
 
Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Stephan Hochdörfer
 
Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Stephan Hochdörfer
 
Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Stephan Hochdörfer
 
Offline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaOffline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaStephan Hochdörfer
 
Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Stephan Hochdörfer
 
Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Stephan Hochdörfer
 
Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Stephan Hochdörfer
 
Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Stephan Hochdörfer
 
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Stephan Hochdörfer
 

More from Stephan Hochdörfer (20)

Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
 
Phing for power users - frOSCon8
Phing for power users - frOSCon8Phing for power users - frOSCon8
Phing for power users - frOSCon8
 
Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8
 
Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13
 
Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13
 
Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13
 
Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13
 
Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13
 
Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13
 
Offline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaOffline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmka
 
Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13
 
Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13
 
Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Testing untestable code - ConFoo13
Testing untestable code - ConFoo13
 
A Phing fairy tale - ConFoo13
A Phing fairy tale - ConFoo13A Phing fairy tale - ConFoo13
A Phing fairy tale - ConFoo13
 
Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13
 
Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12
 
Testing untestable code - IPC12
Testing untestable code - IPC12Testing untestable code - IPC12
Testing untestable code - IPC12
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12
 
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
 

Testing untestable PHP code

  • 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. About me  Stephan Hochdörfer, bitExpert AG  Department Manager Research Labs  enjoying PHP since 1999  S.Hochdoerfer@bitExpert.de  @shochdoerfer
  • 3. Warning! Use at your own risk...
  • 4. No excuse for writing bad code!
  • 5. Seriously, I am not kidding!
  • 6. Theory "There is no secret to writing tests, there are only secrets to write testable code!" Miško Hevery
  • 7. Theory What is „untestable code“?
  • 8. Theory What is „untestable code“? s „new“ is evil!
  • 9. Theory What is „untestable code“?
  • 10. Theory What is „untestable code“?
  • 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. Theory Required Required class class Class to Unittest Class to Unittest Test Test Required Required class class
  • 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. 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. Theory How to achieve „testable“ code?
  • 16. Theory How to achieve „testable“ code? Refactoring
  • 17. Theory "Before you start refactoring, check that you have a solid suite of tests." Martin Fowler, Refactoring
  • 18. Testing „untestable“ PHP Code Let the work begin...
  • 19. Testing „untestable“ PHP Code Safty instructions Do not change existing code!
  • 20. Testing „untestable“ PHP Code | __autoload <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  • 21. 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
  • 22. 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();
  • 23. Testing „untestable“ PHP Code | include_path <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  • 24. 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
  • 25. 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();
  • 26. Testing „untestable“ PHP Code | include_path alternative <?php include('Engine.php'); class Car { private $Engine; public function __construct($sEngine) { $this->Engine = Engine::getByType($sEngine); } }
  • 27. Testing „untestable“ PHP Code | include_path alternative <?php include('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. 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');
  • 29. 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', 'AbstractEngine::get', $content); return $content; } } stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); include('engine.php'); ?>
  • 30. Testing „untestable“ PHP Code How to test private methods?
  • 31. Testing „untestable“ PHP Code How to test private methods? There`s no need to!
  • 32. Testing „untestable“ PHP Code How to test private methods? There`s no need to, but...
  • 33. Testing „untestable“ PHP Code | private vs. protected <?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('private function', 'public function', $content); return $content; } } stream_wrapper_unregister('file'); stream_wrapper_register('file', 'CustomFileStreamWrapper'); include('engine.php'); ?>
  • 34. Testing „untestable“ PHP Code | Namespaces <?php class Car { private $Engine; public function __construct($sEngine) { $this->Engine = CarEngine::getByType($sEngine); } }
  • 35. 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
  • 36. 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); } }
  • 37. 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/
  • 38. 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');
  • 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. 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. 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. 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. 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. 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); }
  • 45. 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
  • 46. 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
  • 47. 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', '....'); }
  • 48. 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', '....'); }
  • 49. 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  Unfortunatley mail() is part of the PHP core and cannot be unloaded
  • 50. 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
  • 51. Testing „untestable“ PHP Code | overwrite internal functions <?php ini_set('runkit.internal_override', '1'); runkit_function_redefine('mail','','return true;'); ?>
  • 53. Generating testable code What else? Remember: No changes to the source code!
  • 54. Generating testable code What else? Generative Programming
  • 55. Generating testable code Generative Programming Configuration Configuration 1 ... n Implementation Implementation Generator Product components Generator Product components Generator Generator application application
  • 56. Generating testable code Generative Programming Configuration Configuration Application Application Implementation Implementation Generator components Generator components Testcases Testcases Generator Generator application application
  • 57. Generating testable code Generative Programming A frame is a data structure for representing knowledge.
  • 58. 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() } }
  • 59. 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() } }
  • 60. Generating testable code Generative Programming Extraction  Show / hide parts of the code Example MailSlot: mail('order@domain.org', 'New sale', '....');
  • 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. Generating testable code Course of action Customizing  Change content of global vars  Pre/Postfixes for own functions, methods, classes Example Prefix: test_
  • 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. Conclusion How much effort to take?
  • 65. Conclusion Conclusion Change your mindset to write testable code!
  • 66. Conclusion Conclusion PHP is a swiss army knife.