2
Your	
  code	
  are	
  my	
  tests
LoneStarPHP	
  2014	
  
Dallas,	
  TX
2
ADVISORY
IN ORDER TO EXPLAIN CERTAIN SITUATIONS YOU MIGHT FACE IN YOUR
DEVELOPMENT CAREER, WE WILL BE DISCUSSING THE USA...
3
Michelangelo van Dam!
!
PHP Consultant
Community Leader
President of PHPBenelux
Contributor
Warming	
  up
4
https://www.flickr.com/photos/bobjagendorf/8535316836
Smallest	
  unit	
  of	
  code
5
https://www.flickr.com/photos/toolstop/4546017269
Example	
  class
<?php !
/** !
 * Example class !
 */ !
class MyClass !
{ !
    /** ... */ !
    public function doSomethi...
Tes9ng	
  for	
  good
   /** ... */!
    public function testClassAcceptsValidRequiredArgument() !
    { !
        $expect...
Tes9ng	
  for	
  bad
    /** !
     * @expectedException InvalidArgumentException !
     */ !
    public function testExce...
We	
  don’t	
  live	
  in	
  a	
  fairy	
  tale!
9
https://www.flickr.com/photos/bertknot/8175214909
Real	
  code,	
  real	
  apps
10
github.com/Telaxus/EPESI
11
Running	
  the	
  project
12
Where	
  are	
  the	
  TESTS?
13
Where	
  are	
  the	
  TESTS?
14
Oh	
  noes,	
  no	
  tests!
15
https://www.flickr.com/photos/mjhagen/2973212926
Let’s	
  get	
  started
16
https://www.flickr.com/photos/npobre/2601582256
How	
  to	
  get	
  about	
  it?
17
SeKng	
  up	
  for	
  tes9ng
<phpunit colors="true" stopOnError="true" stopOnFailure="true">!
<testsuites>!
<testsuite nam...
ModuleManager
• not_loaded_modules	
  
• loaded_modules	
  
• modules	
  
• modules_install	
  
• modules_common	
  
• roo...
ModuleManager::module_install
/** !
 * Includes file with module installation class. !
 * !
 * Do not use directly. !
 * !...
Tes9ng	
  first	
  condi9on
<?php !
!
require_once 'include.php'; !
!
class ModuleManagerTest extends PHPUnit_Framework_Tes...
Run	
  test
22
Check	
  coverage
23
Test	
  for	
  second	
  condi9on
public function testLoadingNonExistingModuleIsNotExecuted() !
{ !
    $module = 'Foo_Bar...
Run	
  tests
25
Check	
  coverage
26
Test	
  for	
  third	
  condi9on
public function testNoInstallationOfModuleWithoutInstallationClass() !
{ !
    $module = ...
Run	
  tests
28
Check	
  code	
  coverage
29
Non-­‐executable	
  code
30
https://www.flickr.com/photos/dazjohnson/7720806824
Test	
  for	
  success
public function testIncludeClassFileForLoadingModule() !
{ !
    $module = 'Base_About'; !
    $res...
Run	
  tests
32
Check	
  code	
  coverage
33
Look	
  at	
  the	
  global	
  coverage
34
Bridging	
  gaps
35
https://www.flickr.com/photos/hugo90/6980712643
Privates	
  exposed
36
http://www.slashgear.com/former-tsa-agent-admits-we-knew-full-body-scanners-didnt-work-31315288/
Dependency
• __construct	
  
• get_module_name	
  
• get_version_min	
  
• get_version_max	
  
• is_saBsfied_by	
  
• requi...
A	
  private	
  constructor!
<?php !
!
defined("_VALID_ACCESS") || die('Direct access forbidden'); !
!
/** !
 * This class...
Don’t	
  touch	
  my	
  junk!
39
https://www.flickr.com/photos/caseymultimedia/5412293730
House	
  of	
  Reflec9on
40
https://www.flickr.com/photos/tabor-roeder/8250770115
Let’s	
  do	
  this…
<?php !
require_once 'include.php'; !
!
class DependencyTest extends PHPUnit_Framework_TestCase !
{ !...
Let’s	
  use	
  the	
  sta9c
$params = array ( !
    'moduleName' => 'Foo_Bar', !
    'minVersion' => 0, !
    'maxVersion...
Use	
  the	
  reflec9on	
  to	
  assert
// Let's retrieve the private properties !
$moduleName = $reflectionClass->getPrope...
Run	
  tests
44
Code	
  Coverage
45
Yes,	
  paradise	
  exists
46
https://www.flickr.com/photos/rnugraha/2003147365
Unit	
  tes9ng	
  is	
  not	
  difficult!
47
You	
  just	
  need	
  to	
  get	
  started
48
PHP	
  has	
  all	
  the	
  tools
49
And	
  there	
  are	
  more	
  	
  
roads	
  to	
  Rome
50
Need	
  help?
51
Michelangelo van Dam
!
michelangelo@in2it.be
@DragonBe
www.in2it.be
Ques9ons?
52
https://www.flickr.com/photos/mdpettitt/8671901426
53
joind.in/10806
Upcoming SlideShare
Loading in...5
×

Your code are my tests - LoneStarPHP 2014

4,770

Published on

After years of promoting PHPUnit I still hear it's hard to get started with unit testing. So instead of showing nice step-by-step examples on how to use PHPUnit, we're going to take examples straight from github and start writing tests for them where I explain the issue, how we test it and how we finally got things tested. So I take on the challenge to start writing tests for PHP projects that already have unit tests in place (like major frameworks) and projects that not even started with unit testing.

Published in: Engineering, Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,770
On Slideshare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
20
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Your code are my tests - LoneStarPHP 2014

  1. 1. 2 Your  code  are  my  tests LoneStarPHP  2014   Dallas,  TX
  2. 2. 2 ADVISORY IN ORDER TO EXPLAIN CERTAIN SITUATIONS YOU MIGHT FACE IN YOUR DEVELOPMENT CAREER, WE WILL BE DISCUSSING THE USAGE OF PRIVATES AND PUBLIC EXPOSURE. IF THESE TOPICS OFFEND OR UPSET YOU, WE WOULD LIKE TO ASK YOU TO LEAVE THIS ROOM NOW. ! THE SPEAKER NOR THE ORGANISATION CANNOT BE HELD ACCOUNTABLE FOR MENTAL DISTRESS OR ANY FORMS OF DAMAGE YOU MIGHT ENDURE DURING OR AFTER THIS PRESENTATION, NOT EVEN IF YOUR NAME IS KEITH CASEY.
  3. 3. 3 Michelangelo van Dam! ! PHP Consultant Community Leader President of PHPBenelux Contributor
  4. 4. Warming  up 4 https://www.flickr.com/photos/bobjagendorf/8535316836
  5. 5. Smallest  unit  of  code 5 https://www.flickr.com/photos/toolstop/4546017269
  6. 6. Example  class <?php ! /** !  * Example class !  */ ! class MyClass ! { !     /** ... */ !     public function doSomething($requiredParam, $optionalParam = null) !     { !         if (!filter_var( !             $requiredParam, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH !         )) { !             throw new InvalidArgumentException('Invalid argument provided'); !         } !         if (null !== $optionalParam) { !             if (!filter_var( !                 $optionalParam, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH !             )) { !                 throw new InvalidArgumentException('Invalid argument provided'); !             } !             $requiredParam .= ' - ' . $optionalParam; !         } !         return $requiredParam; !     } ! } 6
  7. 7. Tes9ng  for  good    /** ... */!     public function testClassAcceptsValidRequiredArgument() !     { !         $expected = $argument = 'Testing PHP Class'; !         $myClass = new MyClass; !         $result = $myClass->doSomething($argument); !         $this->assertSame($expected, $result,  !             'Expected result differs from actual result'); !     } ! !    /** ... */     !     public function testClassAcceptsValidOptionalArgument() !     { !         $requiredArgument = 'Testing PHP Class'; !         $optionalArgument = 'Is this not fun?!?'; !         $expected = $requiredArgument . ' - ' . $optionalArgument; !         $myClass = new MyClass; !         $result = $myClass->doSomething($requiredArgument, $optionalArgument); !         $this->assertSame($expected, $result,  !             'Expected result differs from actual result'); !     } 7
  8. 8. Tes9ng  for  bad     /** !      * @expectedException InvalidArgumentException !      */ !     public function testExceptionIsThrownForInvalidRequiredArgument() !     { !         $expected = $argument = new StdClass; !         $myClass = new MyClass; !         $result = $myClass->doSomething($argument); !         $this->assertSame($expected, $result,  !             'Expected result differs from actual result'); !     } !      !     /** !      * @expectedException InvalidArgumentException !      */ !     public function testExceptionIsThrownForInvalidOptionalArgument() !     { !         $requiredArgument = 'Testing PHP Class'; !         $optionalArgument = new StdClass; !         $myClass = new MyClass; !         $result = $myClass->doSomething($requiredArgument, $optionalArgument); !         $this->assertSame($expected, $result,  !             'Expected result differs from actual result'); !     } 8
  9. 9. We  don’t  live  in  a  fairy  tale! 9 https://www.flickr.com/photos/bertknot/8175214909
  10. 10. Real  code,  real  apps 10
  11. 11. github.com/Telaxus/EPESI 11
  12. 12. Running  the  project 12
  13. 13. Where  are  the  TESTS? 13
  14. 14. Where  are  the  TESTS? 14
  15. 15. Oh  noes,  no  tests! 15 https://www.flickr.com/photos/mjhagen/2973212926
  16. 16. Let’s  get  started 16 https://www.flickr.com/photos/npobre/2601582256
  17. 17. How  to  get  about  it? 17
  18. 18. SeKng  up  for  tes9ng <phpunit colors="true" stopOnError="true" stopOnFailure="true">! <testsuites>! <testsuite name="EPESI admin tests">! <directory phpVersion="5.3.0">tests/admin</directory>! </testsuite>! <testsuite name="EPESI include tests">! <directory phpVersion="5.3.0">tests/include</directory>! </testsuite>! <testsuite name="EPESI modules testsuite">! <directory phpVersion="5.3.0">tests/modules</directory>! </testsuite>! </testsuites>! <php>! <const name="DEBUG_AUTOLOADS" value="1"/>! <const name="CID" value="1234567890123456789"/>! </php>! <logging>! <log type="coverage-html" target="build/coverage" charset="UTF-8"/>! <log type="coverage-clover" target="build/logs/clover.xml"/>! <log type="junit" target="build/logs/junit.xml"/>! </logging>! </phpunit> 18
  19. 19. ModuleManager • not_loaded_modules   • loaded_modules   • modules   • modules_install   • modules_common   • root   • processing   • processed_modules   • include_install   • include_common   • include_main   • create_load_priority_array   • check_dependencies   • saBsfy_dependencies   • get_module_dir_path   • get_module_file_name   • list_modules   • exists   • register   • unregister   • is_installed   • upgrade   • downgrade   • get_module_class_name   • install   • uninstall   • get_processed_modules   • get_load_priority_array   • new_instance   • get_instance   • create_data_dir   • remove_data_dir   • get_data_dir   • load_modules   • create_common_cache   • create_root   • check_access   • call_common_methods   • check_common_methods   • required_modules   • reset_cron 19
  20. 20. ModuleManager::module_install /** !  * Includes file with module installation class. !  * !  * Do not use directly. !  * !  * @param string $module_class_name module class name - underscore separated !  */ ! public static final function include_install($module_class_name) { !     if(isset(self::$modules_install[$module_class_name])) return true; !     $path = self::get_module_dir_path($module_class_name); !     $file = self::get_module_file_name($module_class_name); !     $full_path = 'modules/' . $path . '/' . $file . 'Install.php'; !     if (!file_exists($full_path)) return false; !     ob_start(); !     $ret = require_once($full_path); !     ob_end_clean(); !     $x = $module_class_name.'Install'; !     if(!(class_exists($x, false)) || ! !array_key_exists('ModuleInstall',class_parents($x))) !         trigger_error('Module '.$path.': Invalid install file',E_USER_ERROR); !     self::$modules_install[$module_class_name] = new $x($module_class_name); !     return true; ! } 20
  21. 21. Tes9ng  first  condi9on <?php ! ! require_once 'include.php'; ! ! class ModuleManagerTest extends PHPUnit_Framework_TestCase ! { !     protected function tearDown() !     { !         ModuleManager::$modules_install = array (); !     } ! !     public function testReturnImmediatelyWhenModuleAlreadyLoaded() !     { !         $module = 'Foo_Bar'; !         ModuleManager::$modules_install[$module] = 1; !         $result = ModuleManager::include_install($module); !         $this->assertTrue($result, !             'Expecting that an already installed module returns true'); !         $this->assertCount(1, ModuleManager::$modules_install, !             'Expecting to find 1 module ready for installation'); !     } ! } 21
  22. 22. Run  test 22
  23. 23. Check  coverage 23
  24. 24. Test  for  second  condi9on public function testLoadingNonExistingModuleIsNotExecuted() ! { !     $module = 'Foo_Bar'; !     $result = ModuleManager::include_install($module); !     $this->assertFalse($result, 'Expecting failure for loading Foo_Bar'); !     $this->assertEmpty(ModuleManager::$modules_install, !         'Expecting to find no modules ready for installation'); ! } 24
  25. 25. Run  tests 25
  26. 26. Check  coverage 26
  27. 27. Test  for  third  condi9on public function testNoInstallationOfModuleWithoutInstallationClass() ! { !     $module = 'EssClient_IClient'; !     $result = ModuleManager::include_install($module); !     $this->assertFalse($result, 'Expecting failure for loading Foo_Bar'); !     $this->assertEmpty(ModuleManager::$modules_install, !         'Expecting to find no modules ready for installation'); ! } 27
  28. 28. Run  tests 28
  29. 29. Check  code  coverage 29
  30. 30. Non-­‐executable  code 30 https://www.flickr.com/photos/dazjohnson/7720806824
  31. 31. Test  for  success public function testIncludeClassFileForLoadingModule() ! { !     $module = 'Base_About'; !     $result = ModuleManager::include_install($module); !     $this->assertTrue($result, 'Expected module to be loaded'); !     $this->assertCount(1, ModuleManager::$modules_install, !         'Expecting to find 1 module ready for installation'); ! } 31
  32. 32. Run  tests 32
  33. 33. Check  code  coverage 33
  34. 34. Look  at  the  global  coverage 34
  35. 35. Bridging  gaps 35 https://www.flickr.com/photos/hugo90/6980712643
  36. 36. Privates  exposed 36 http://www.slashgear.com/former-tsa-agent-admits-we-knew-full-body-scanners-didnt-work-31315288/
  37. 37. Dependency • __construct   • get_module_name   • get_version_min   • get_version_max   • is_saBsfied_by   • requires   • requires_exact   • requires_at_least   • requires_range 37
  38. 38. A  private  constructor! <?php ! ! defined("_VALID_ACCESS") || die('Direct access forbidden'); ! ! /** !  * This class provides dependency requirements !  * @package epesi-base !  * @subpackage module  !  */ ! class Dependency { ! !     private $module_name; !     private $version_min; !     private $version_max; !     private $compare_max; ! !     private function __construct(! $module_name, $version_min, $version_max, $version_max_is_ok = true) { !         $this->module_name = $module_name; !         $this->version_min = $version_min; !         $this->version_max = $version_max; !         $this->compare_max = $version_max_is_ok ? '<=' : '<'; !     } ! !     /** ... */ ! } 38
  39. 39. Don’t  touch  my  junk! 39 https://www.flickr.com/photos/caseymultimedia/5412293730
  40. 40. House  of  Reflec9on 40 https://www.flickr.com/photos/tabor-roeder/8250770115
  41. 41. Let’s  do  this… <?php ! require_once 'include.php'; ! ! class DependencyTest extends PHPUnit_Framework_TestCase ! { !     public function testConstructorSetsProperSettings() !     { !         require_once 'include/module_dependency.php'; ! !         // We have a problem, the constructor is private!!     } ! } 41
  42. 42. Let’s  use  the  sta9c $params = array ( !     'moduleName' => 'Foo_Bar', !     'minVersion' => 0, !     'maxVersion' => 1, !     'maxOk' => true, ! ); ! // We use a static method for this test ! $dependency = Dependency::requires_range( !     $params['moduleName'], !     $params['minVersion'], !     $params['maxVersion'], !     $params['maxOk'] ! ); ! ! // We use reflection to see if properties are set correctly ! $reflectionClass = new ReflectionClass('Dependency'); 42
  43. 43. Use  the  reflec9on  to  assert // Let's retrieve the private properties ! $moduleName = $reflectionClass->getProperty('module_name'); ! $moduleName->setAccessible(true); ! $minVersion = $reflectionClass->getProperty('version_min'); ! $minVersion->setAccessible(true); ! $maxVersion = $reflectionClass->getProperty('version_max'); ! $maxVersion->setAccessible(true); ! $maxOk = $reflectionClass->getProperty('compare_max'); ! $maxOk->setAccessible(true); ! ! // Let's assert ! $this->assertEquals($params['moduleName'], $moduleName->getValue($dependency), !     'Expected value does not match the value set’);! ! $this->assertEquals($params['minVersion'], $minVersion->getValue($dependency), !     'Expected value does not match the value set’);! ! $this->assertEquals($params['maxVersion'], $maxVersion->getValue($dependency), !     'Expected value does not match the value set’);! ! $this->assertEquals('<=', $maxOk->getValue($dependency), !     'Expected value does not match the value set'); 43
  44. 44. Run  tests 44
  45. 45. Code  Coverage 45
  46. 46. Yes,  paradise  exists 46 https://www.flickr.com/photos/rnugraha/2003147365
  47. 47. Unit  tes9ng  is  not  difficult! 47
  48. 48. You  just  need  to  get  started 48
  49. 49. PHP  has  all  the  tools 49
  50. 50. And  there  are  more     roads  to  Rome 50
  51. 51. Need  help? 51 Michelangelo van Dam ! michelangelo@in2it.be @DragonBe www.in2it.be
  52. 52. Ques9ons? 52 https://www.flickr.com/photos/mdpettitt/8671901426
  53. 53. 53 joind.in/10806
  1. A particular slide catching your eye?

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

×