Your code are my tests - LoneStarPHP 2014
Upcoming SlideShare
Loading in...5
×
 

Your code are my tests - LoneStarPHP 2014

on

  • 2,703 views

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

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.

Statistics

Views

Total Views
2,703
Views on SlideShare
1,079
Embed Views
1,624

Actions

Likes
4
Downloads
11
Comments
0

14 Embeds 1,624

http://www.dragonbe.com 926
http://webmastah.pl 641
http://feedly.com 19
http://librosweb.es 9
http://plus.url.google.com 8
http://dragonbe2.rssing.com 5
https://twitter.com 3
http://feeds.feedburner.com 3
http://www.inoreader.com 3
http://digg.com 2
http://www.google.com 2
http://www.feedspot.com 1
http://prlog.ru 1
http://www.200please.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Your code are my tests - LoneStarPHP 2014 Your code are my tests - LoneStarPHP 2014 Presentation Transcript

  • 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 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 Michelangelo van Dam! ! PHP Consultant Community Leader President of PHPBenelux Contributor View slide
  • Warming  up 4 https://www.flickr.com/photos/bobjagendorf/8535316836 View slide
  • Smallest  unit  of  code 5 https://www.flickr.com/photos/toolstop/4546017269
  • 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
  • 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
  • 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
  • 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 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
  • 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
  • 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
  • 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
  • Run  test 22
  • Check  coverage 23
  • 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
  • Run  tests 25
  • Check  coverage 26
  • 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
  • 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'; !     $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
  • 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   • requires   • requires_exact   • requires_at_least   • requires_range 37
  • 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
  • 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 ! { !     public function testConstructorSetsProperSettings() !     { !         require_once 'include/module_dependency.php'; ! !         // We have a problem, the constructor is private!!     } ! } 41
  • 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
  • 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
  • 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