SlideShare a Scribd company logo
Day Camp
4 Developers

Day Camp
4 Developers

&

2

Unit Testing PHP apps 	

with PHPUnit
Michelangelo	
  van	
  Dam

2
Let’s	
  talk	
  about	
  tes6ng

3
Excuses	
  not	
  to	
  test
• No	
  6me	
  
• Not	
  in	
  budget	
  
• We	
  don’t	
  know	
  how	
  	
  
-­‐

valid	
  and	
  honest	
  answer	
  

-­‐

right,	
  like	
  that’s	
  going	
  to	
  happen	
  

• We	
  add	
  unit	
  tests	
  a@er	
  finish	
  project	
  
•…

4
No	
  excuses

5
Unit	
  tes6ng	
  is	
  fun	
  and	
  easy!

• When	
  you	
  write	
  code	
  
• You	
  already	
  test	
  in	
  your	
  head	
  
• Write	
  out	
  these	
  test(s)	
  
• And	
  protect	
  your	
  code	
  base

6
How	
  to	
  get	
  started?

7
My	
  example	
  code
• Get	
  this	
  example	
  from	
  GitHub	
  
-­‐

hQps://github.com/in2it/Utexamples

project/
src/
Utexamples/
Calculator.php
autoload.php
tests/
Utexamples/
CalculatorTest.php

8
Simple	
  example	
  class
• Add	
  by	
  one	
  

-­‐ adds	
  the	
  current	
  value	
  by	
  one

?php !
namespace Utexamples; !

!

/** !
 * Class that allows us to make all sorts of calculations !
 */ !
class Calculator !
{ !
    protected $_value = 0; !

!

    /** !
     * Adds the current value by one !
     * !
     * @return int The value from this method !
     */ !
    public function addByOne() !
    { !
        $this-_value++; !
        return $this-_value; !
    } !
}
9
Our	
  unit	
  test
?php !
namespace Utexamples; !

!

Class CalculatorTest extends PHPUnit_Framework_TestCase !
{ !
    public function testCalculatorCanAddByOne() !
    { !
        $calculator = new Calculator(); !
        $result = $calculator-addByOne(); !
        $this-assertSame(1, $result); !
    } !
}

10
My	
  autoloader
?php !

!

/** !
 * Simple autoloader that follow the PHP Standards Recommendation #0 (PSR-0) !
 * @see https://github.com/php-fig/fig-standards/blob/master/accepted/
PSR-0.md for more informations. !
 * !
 * Code inspired from the SplClassLoader RFC !
 * @see https://wiki.php.net/rfc/splclassloader#example_implementation !
 */ !
spl_autoload_register(function($className) { !
    $className = ltrim($className, ''); !
    $fileName = ''; !
    $namespace = ''; !
    if ($lastNsPos = strripos($className, '')) { !
        $namespace = substr($className, 0, $lastNsPos); !
        $className = substr($className, $lastNsPos + 1); !
        $fileName = str_replace(!
'', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; !
    } !
    $fileName = __DIR__ . DIRECTORY_SEPARATOR . $fileName . $className . '.php'; !
    if (file_exists($fileName)) { !
        require $fileName; !

!

        return true; !
    } !

!

    return false; !
});
11
Running	
  PHPUnit

12
Running	
  PHPUnit

12
A	
  lot	
  of	
  parameters!

• Easily	
  possible	
  to	
  make	
  mistakes	
  
• Every	
  developer	
  might	
  use	
  different	
  params	
  
• Not	
  easy	
  for	
  (semi-­‐)automated	
  tes6ng	
  
-­‐

Using	
  IDE	
  for	
  running	
  unit	
  tests

13
Let’s	
  op6mise	
  this
?xml version=1.0 encoding=UTF-8?
!
!-- file: project/phpunit.xml --
!
phpunit bootstrap=./src/autoload.php colors=true
!
testsuite name=Unit Test Example code
directory./tests/directory
/testsuite
!
filter
whitelist
directory suffix=.php./src/directory
exclude
directory suffix=.phtml./src/directory
/exclude
/whitelist
/filter
!
/phpunit

14
Now	
  run	
  more	
  relaxed

15
Now	
  run	
  more	
  relaxed

15
We’re	
  no	
  grads	
  anymore

16
Real	
  apps,	
  real	
  money

17
18
How	
  to	
  reach	
  the	
  top

19
Data	
  Model	
  Tes6ng

20
Simple	
  Product	
  Model
Product
_productId : integer
_code : string
_title : string
_description : string
_image : string
_price : float
_created : DateTime
_modified : DateTime
__construct($params : null | array)
setProductId($productId : integer) : Product
getProductId() : integer
setCode($code : string) : Product
getCode() : string
setTitle($title : string) : Product
getTitle() : string
setDescription($description : string) : Product
getDescription() : string
setImage($image : string) : Product
getImage() : string
setPrice($price : float) : Product
getPrice() : float
setCreated($created : string | DateTime) : Product
getCreated() : DateTime
setModified($modified : string | DateTime) : Product
getModified() : DateTime
populate($data : array)
toArray() : array
__toString() : string
21
A	
  simple	
  ProductTest
?php !
namespace UtexamplesModel; !
/** !
 * Class ProductTest !
 * @package UtexamplesModel !
 * @group Model !
 */ !
class ProductTest extends PHPUnit_Framework_TestCase !
{ !
    public function testProductCanBePopulated() !
    { !
        $data = array ( !
            'productId' = 1, !
            'code' = 'TSTPROD1', !
            'title' = 'Test product 1', !
            'description' = 'This is a full description of test product 1', !
            'image' = 'image.png', !
            'price' = 123.95, !
            'created' = '2013-11-20 16:00:00', !
            'modified' = '2013-11-20 17:00:00', !
        ); !

!

        $product = new Product($data); !
        $this-assertEquals($data, $product-toArray()); !
    } !
}
22
A	
  simple	
  ProductTest
?php !
namespace UtexamplesModel; !
/** !
 * Class ProductTest !
 * @package UtexamplesModel !
 * @group Model !
 */ !
class ProductTest extends PHPUnit_Framework_TestCase !
{ !
    public function testProductCanBePopulated() !
    { !
        $data = array ( !
            'productId' = 1, !
            'code' = 'TSTPROD1', !
            'title' = 'Test product 1', !
            'description' = 'This is a full description of test product 1', !
            'image' = 'image.png', !
            'price' = 123.95, !
            'created' = '2013-11-20 16:00:00', !
            'modified' = '2013-11-20 17:00:00', !
        ); !

!
        $product = new Product($data); !
        $product = new Product($data); !
        $this-assertEquals($data, $product-toArray());
        $this-assertEquals($data, $product-toArray()); !
    } !
}
22
Running	
  the	
  test

23
Running	
  the	
  test

23
data	
  fixture
    public function goodDataProvider() { !
        return array ( !
            array ( !
                1, !
                'TSTPROD1', !
                'Test Product 1', !
                'This is a full description of test product 1', !
                'image.png', !
                123.95, !
                '2013-11-20 16:00:00', !
                '2013-11-20 17:00:00', !
            ), !
            array ( !
                2, !
                'TSTPROD2', !
                'Test Product 2', !
                'This is a full description of test product 2', !
                'image.png', !
                4125.99, !
                '2013-11-20 16:00:00', !
                '2013-11-20 17:00:00', !
            ), !
        ); !
    }

24
Using	
  @dataProvider
    /** !
     * @dataProvider goodDataProvider !
     */ !
    public function testProductCanBePopulated( !
        $productId, $code, $title, $description, $image, $price, $created, $modified !
    ) !
    { !
        $data = array ( !
            'productId' = $productId, !
            'code' = $code, !
            'title' = $title, !
            'description' = $description, !
            'image' = $image, !
            'price' = $price, !
            'created' = $created, !
            'modified' = $modified, !
        ); !

!

        $product = new Product($data); !
        $this-assertEquals($data, $product-toArray()); !
    }

25
Using	
  @dataProvider
    /** !
    /** !
     * @dataProvider goodDataProvider !
     * @dataProvider goodDataProvider !
     */ !
     */
    public function testProductCanBePopulated( !
        $productId, $code, $title, $description, $image, $price, $created, $modified !
    ) !
    { !
        $data = array ( !
            'productId' = $productId, !
            'code' = $code, !
            'title' = $title, !
            'description' = $description, !
            'image' = $image, !
            'price' = $price, !
            'created' = $created, !
            'modified' = $modified, !
        ); !

!

        $product = new Product($data); !
        $this-assertEquals($data, $product-toArray()); !
    }

25
Running	
  with	
  @dataProvider

26
Running	
  with	
  @dataProvider

26
To	
  protect	
  and	
  to	
  serve

27
OWASP	
  top	
  10	
  exploits

https://www.owasp.org/index.php/Top_10_2013-Top_10
28
Filtering	
  	
  Valida6on

29
Libs	
  you	
  can	
  use
• Zend	
  Framework	
  1:	
  Zend_Filter_Input	
  
• Zend	
  Framework	
  2:	
  ZendInputFilter	
  
• Symfony:	
  SymfonyComponentValidator	
  
• Aura:	
  AuraFrameworkInputFilter	
  
• Lithium:	
  lithiumu6lValidator	
  
• Laravel:	
  AppValidator
30
Modify	
  our	
  Product	
  class
?php !
namespace UtexamplesModel; !

!

class Product extends ModelAbstract !
{ !
...!
    /** !
     * @var Zend_Filter_Input The filter/validator for this Product !
     */ !
    protected $_inputFilter; !

!

    /** !
     * @var bool The validation of data for this Product !
     */ !
    protected $_valid; !

!

    /** !
     * Helper class to create filter and validation rules !
     * !
     * @access protected !
     */ !
    protected function _createInputFilter() !
    { !
...!
    } !
...!
}

31
_createInputFilter()
    protected function _createInputFilter() !
    { !
        $filters = array ( !
            'productId' = array('Int'), !
            'code' = array ('StripTags', 'StringTrim', 'StringToUpper'), !
            'title' = array ('StripTags', 'StringTrim'), !
            'description' = array ('StripTags', 'StringTrim'), !
            'image' = array ('StripTags', 'StringTrim','StringToLower'), !
            'price' = array (), !
        ); !
        $validators = array ( !
            'productId' = array ( !
                'Int', !
                array ('GreaterThan', array ('min' = 0, 'inclusive' = true)), !
            ), !
            'code' = array ( !
                'Alnum', !
                array ('StringLength', array ('min' = 5, 'max' = 50)), !
            ), !
            'title' = array ('NotEmpty'), !
            'description' = array ('NotEmpty'), !
            'image' = array ('NotEmpty'), !
            'price' = array ( !
                'Float', !
                array ('GreaterThan', array ('min' = 0, 'inclusive' = true)), !
            ), !
        ); !
        $this-_inputFilter = new Zend_Filter_Input($filters, $validators); !
    }
32
Modify	
  your	
  seQers
    /** !
     * Set the product code for this Product !
     * !
     * @param string $code !
     * @return Product !
     */ !
    public function setCode($code) !
    { !
        $this-_inputFilter-setData(array ('code' = $code)); !
        if ($this-_inputFilter-isValid('code')) { !
            $this-_code = $this-_inputFilter-code; !
            $this-setValid(true); !
        } else { !
            $this-setValid(false); !
        } !
        return $this; !
    }

33
Modify	
  your	
  seQers
    /** !
     * Set the product code for this Product !
     * !
     * @param string $code !
     * @return Product !
     */ !
    public function setCode($code) !
        $this-_inputFilter-setData(array ('code' = $code));
    { !
        if ($this-_inputFilter-isValid('code')) { !
        $this-_inputFilter-setData(array ('code' = $code)); !
            $this-_code = $this-_inputFilter-code; !
        if ($this-_inputFilter-isValid('code')) { !
            $this-_code = $this-_inputFilter-code; !
            $this-setValid(true); !
            $this-setValid(true); !
        } else { !
        } else { !
            $this-setValid(false); !
            $this-setValid(false); !
        } ! !
        }
        return $this; !
        return $this;
    }

!

33
Using	
  dataproviders	
  again
    public function badDataProvider() !
    { !
        return array ( !
            array ( !
                1, '', '', '', '', 0, !
                '0000-00-00 00:00:00', !
                '0000-00-00 00:00:00', !
            ), !
            array ( !
                1, !
                '!@#$%^@^*{}[]=-/'', 'Test Product 1', !
                'This is a full description of test product 1', 'image.png', !
                123.95, '2013-11-20 16:00:00', '2013-11-20 17:00:00', !
            ), !
            array ( !
                1, '' OR 1=1; --', 'Test Product 1', !
                'This is a full description of test product 1', 'image.png', !
                123.95, '2013-11-20 16:00:00', '2013-11-20 17:00:00', !
            ), !
        ); !
    }

34
And	
  now	
  we	
  test	
  for	
  valid	
  data
    /** !
     * @dataProvider badDataProvider !
     */ !
    public function testProductRejectsBadData( !
        $productId, $code, $title, $description, $image, $price, $created, $modified !
    ) !
    { !
        $data = array ( !
            'productId' = $productId, !
            'code' = $code, !
            'title' = $title, !
            'description' = $description, !
            'image' = $image, !
            'price' = $price, !
            'created' = $created, !
            'modified' = $modified, !
        ); !

!

        $product = new Product($data); !
        $this-assertFalse($product-isValid()); !
    }

35
And	
  now	
  we	
  test	
  for	
  valid	
  data
    /** !
     * @dataProvider badDataProvider !
     */ !
    public function testProductRejectsBadData( !
        $productId, $code, $title, $description, $image, $price, $created, $modified !
    ) !
    { !
        $data = array ( !
            'productId' = $productId, !
            'code' = $code, !
            'title' = $title, !
            'description' = $description, !
            'image' = $image, !
            'price' = $price, !
            'created' = $created, !
            'modified' = $modified, !
        ); !

!

        $product = new Product($data); !
        $product = new Product($data); !
        $this-assertFalse($product-isValid()); !
        $this-assertFalse($product-isValid());
    }

35
Running	
  our	
  tests

36
Running	
  our	
  tests

36
Databases,	
  the	
  wisdom	
  fountain

37
4	
  stages	
  of	
  database	
  tes6ng

• Setup	
  table	
  fixtures	
  
• Run	
  tests	
  on	
  the	
  database	
  interac6ons	
  
• Verify	
  results	
  of	
  tests	
  
• Teardown	
  the	
  fixtures

38
Data	
  fixture	
  data	
  set

39
Hello	
  DBUnit
?php !
namespace UtexamplesModel; !

!

use PHPUnit_Extensions_Database_DataSet_IDataSet; !
use PHPUnit_Extensions_Database_DB_IDatabaseConnection; !
use PHPUnit_Extensions_Database_DataSet_QueryDataSet; !

!

class ProductDbTest extends PHPUnit_Extensions_Database_TestCase !
{ !
    protected $_pdo; !

!

    public function __construct() !
    { !
        $this-_pdo = new PDO('sqlite::memory:'); !
        $this-_pdo-exec(!
file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!
); !
    } !

!

    final public function getConnection() !
    { !
        return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
    } !

!

    protected function getDataSet() !
    { !
        return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !
    } !
}
40
Hello	
  DBUnit
?php !
namespace UtexamplesModel; !

!

use PHPUnit_Extensions_Database_DataSet_IDataSet; !
use PHPUnit_Extensions_Database_DB_IDatabaseConnection; !
use PHPUnit_Extensions_Database_DataSet_QueryDataSet; !

!

class ProductDbTest extends PHPUnit_Extensions_Database_TestCase !
{ !
    protected $_pdo; !

protected $_pdo; !
!
!
    public function __construct()

!

    { !
public function __construct() !
        $this-_pdo = new PDO('sqlite::memory:'); !
{ !
        $this-_pdo-exec(!
    $this-_pdo = new PDO('sqlite::memory:'); !
file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!
    $this-_pdo-exec(!
); !
    } ! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!

!

); !

    final public function getConnection() !
}
    { !
        return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
    } !

!

    protected function getDataSet() !
    { !
        return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !
    } !
}
40
Hello	
  DBUnit
?php !
namespace UtexamplesModel; !

!

use PHPUnit_Extensions_Database_DataSet_IDataSet; !
use PHPUnit_Extensions_Database_DB_IDatabaseConnection; !
use PHPUnit_Extensions_Database_DataSet_QueryDataSet; !

!

class ProductDbTest extends PHPUnit_Extensions_Database_TestCase !
{ !
    protected $_pdo; !

!

    public function __construct() !
    { !
        $this-_pdo = new PDO('sqlite::memory:'); !
        $this-_pdo-exec(!
file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!
); !
final public function getConnection() !
    } !

{
!

!
    return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
    final public function getConnection() !
    { !
}
        return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
    } !

!

    protected function getDataSet() !
    { !
        return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !
    } !
}
40
Hello	
  DBUnit
?php !
namespace UtexamplesModel; !

!

use PHPUnit_Extensions_Database_DataSet_IDataSet; !
use PHPUnit_Extensions_Database_DB_IDatabaseConnection; !
use PHPUnit_Extensions_Database_DataSet_QueryDataSet; !

!

class ProductDbTest extends PHPUnit_Extensions_Database_TestCase !
{ !
    protected $_pdo; !

!

    public function __construct() !
    { !
        $this-_pdo = new PDO('sqlite::memory:'); !
        $this-_pdo-exec(!
file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!
); !
    } !

!

    final public function getConnection() !
    { !
protected function getDataSet() !
        return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
{ !
    } !

    return $this-createFlatXMLDataSet(!
!

dirname(__DIR__) . ‘/_files/initialDataSet.xml'!
    protected function getDataSet() !
    {); !
!
        return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !
}
    } !
}
40
Hello	
  DBUnit
?php !
namespace UtexamplesModel; !

!

use PHPUnit_Extensions_Database_DataSet_IDataSet; !
use PHPUnit_Extensions_Database_DB_IDatabaseConnection; !
use PHPUnit_Extensions_Database_DataSet_QueryDataSet; !

!

class ProductDbTest extends PHPUnit_Extensions_Database_TestCase !
{ !
    protected $_pdo; !

!

    public function __construct() !
    { !
        $this-_pdo = new PDO('sqlite::memory:'); !
        $this-_pdo-exec(!
file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!
); !
    } !

!

    final public function getConnection() !
    { !
protected function getDataSet() !
        return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !
{ !
    } !

    return $this-createFlatXMLDataSet(!
!

dirname(__DIR__) . ‘/_files/initialDataSet.xml'!
    protected function getDataSet() !
    {); !
!
        return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !
}
    } !
}
40
Ini6alDataset
?xml version=1.0 encoding=UTF-8?
dataset
product
productId=1
code=TEST CODE1
title=First test product
description=This is our first test product
image=http://www.example.com/image/image.png
price=150.95
created=2013-03-30 10:11:12
modified=2013-12-11 09:08:07/
product
productId=2
code=TEST CODE2
title=Second test product
description=This is our second test product
image=http://www.example.com/image/image.png
price=19999.00
created=2013-03-30 10:11:12
modified=2013-12-11 09:08:07/
product
productId=3
code=TEST CODE3
title=Third test product
description=This is our third test product
image=http://www.example.com/image/image.png
price=0.45
created=2013-03-30 10:11:12
modified=2013-12-11 09:08:07/
/dataset
41
First	
  DB	
  Test
    public function testProductsCanBeLoadedFromDatabase() !
    { !
        $currentDataset = $this-getDataSet(); !

!

        $expectedDataset = $this-createFlatXmlDataSet( !
            dirname(__DIR__) . '/_files/selectDataSet.xml' !
        ); !

!

        $this-assertDataSetsEqual($expectedDataset, $currentDataset); !
    }

42
Adding	
  Data	
  Test
    public function testProductAddToDatabase() !
    { !
        $data = array ( !
            'code' = 'TST', !
            'title' = 'Test', !
            'description' = 'Testing Test', !
            'image' = 'http://www.example.com/image.png', !
            'price' = 10.00, !
            'created' = '2013-12-15 01:55:00', !
            'modified' = '2013-12-20 16:00:00', !
        ); !

!

        $product = new Product($data); !
        $product-setPdo($this-_pdo); !
        $product-save(); !

!

        $expectedDs = $this-createFlatXMLDataSet( !
            dirname(__DIR__) . '/_files/addProductDataSet.xml' !
        ); !
        $currentDs = $this-getConnection()-createDataSet(array ('product')); !
        $this-assertDataSetsEqual($expectedDs, $currentDs); !
    }

43
addProductDataSet.xml
?xml version=1.0 encoding=UTF-8?
dataset
...
product
productId=4
code=TEST
title=Test
description=Testing Test
image=http://www.example.com/image.png
price=10.0
created=2013-12-15 01:55:00
modified=2013-12-20 16:00:00”/
/dataset

44
Running	
  our	
  DBUnit	
  test

45
Running	
  our	
  DBUnit	
  test

45
Oops

46
Oops

46
Oops

46
Oh	
  no,	
  I	
  made	
  a	
  TYPO!

47
addProductDataSet.xml
?xml version=1.0 encoding=UTF-8?
dataset
...
product
productId=4
code=TST
title=Test
description=Testing Test
image=http://www.example.com/image.png
price=10.0
created=2013-12-15 01:55:00
$data = array ( !
modified=2013-12-20 16:00:00”/
    'code' = 'TST', !
/dataset
    'title' = 'Test', !
    'description' = 'Testing Test', !
    'image' = 'http://www.example.com/image.png', !
    'price' = 10.00, !
    'created' = '2013-12-15 01:55:00', !
    'modified' = '2013-12-20 16:00:00', !
);

48
Running	
  our	
  DBUnit	
  test

49
Running	
  our	
  DBUnit	
  test

49
Everybody	
  happy

50
Some	
  downsides
• Tes6ng	
  databases	
  takes	
  6me	
  
-­‐
-­‐
-­‐
-­‐
-­‐

create	
  a	
  real	
  database	
  connec6on	
  
reini6alise	
  the	
  database	
  (load	
  schema,	
  truncate	
  tables)	
  
load	
  ini6al	
  state	
  before	
  test	
  (with	
  each	
  test)	
  
execute	
  on	
  the	
  database	
  
compare	
  expected	
  result	
  with	
  actual	
  result

51
We	
  can	
  do	
  beQer!

52
Mock	
  objects
• They	
  replace	
  an	
  object	
  for	
  tes6ng	
  
-­‐
-­‐

a	
  class	
  with	
  all	
  methods	
  
a	
  class	
  with	
  a	
  single	
  method	
  

-­‐
-­‐
-­‐

databases	
  
web	
  services	
  
file	
  systems	
  

-­‐

once	
  you	
  got	
  everything	
  set	
  up

• Since	
  they	
  replace	
  “expansive”	
  connec6ons	
  
• Are	
  quicker	
  and	
  more	
  reliable	
  to	
  test	
  
53
Same	
  tests,	
  but	
  now	
  mocked
?php !

!

namespace UtexamplesModel; !

!

use PDO; !
use PDOStatement; !

!

class ProductMockTest extends PHPUnit_Framework_TestCase !
{ !
    public function testProductsCanBeLoadedFromDatabase() !
    { !

!

    } !

!

    public function testProductAddToDatabase() !
    { !

!

    } !
}

54
testProductsCanBeLoadedFromDatabase
    public function testProductsCanBeLoadedFromDatabase() !
    { !
        $data = array (); !
        // let's mock the prepare statement !
        $pdostmt = $this-getMock('PDOStatement', array ('execute', 'fetchAll')); !
        $pdostmt-expects($this-atLeastOnce()) !
            -method('execute') !
            -will($this-returnValue(true)); !
        $pdostmt-expects($this-atLeastOnce()) !
            -method('fetchAll') !
            -will($this-returnValue($data)); !
        // let's mock the PDO object and return the mocked statement !
        $pdo = $this-getMock('PDO', array ('prepare'), array ('sqlite::memory')); !
        $pdo-expects($this-atLeastOnce()) !
            -method('prepare') !
            -will($this-returnValue($pdostmt)); !

!

        $productCollection = new ProductCollection(); !
        $productCollection-setPdo($pdo); !
        $productCollection-fetchAll(); !

!

        $this-assertEquals($data, $productCollection-toArray()); !
    }

55
My	
  $data	
  array
        $data = array ( !
            array ( !
                'productId' = 1, !
                'code' = 'TST1', !
                'title' = 'Test 1', !
                'description' = 'Testing product 1', !
                'image' = 'http://www.example.com/image1.png', !
                'price' = 10.00, !
                'created' = '2013-12-01 01:55:00', !
                'modified' = '2013-12-20 16:00:00', !
            ), !
            array ( !
                'productId' = 2, !
                'code' = 'TST2', !
                'title' = 'Test 2', !
                'description' = 'Testing product 2', !
                'image' = 'http://www.example.com/image2.png', !
                'price' = 199.95, !
                'created' = '2013-12-02 02:55:00', !
                'modified' = '2013-12-20 16:00:00', !
            ), !
        );

56
testProductAddToDatabase
    public function testProductAddToDatabase() !
    { !
        $pdostmt = $this-getMock('PDOStatement', array ('execute')); !
        $pdostmt-expects($this-atLeastOnce()) !
            -method('execute') !
            -will($this-returnValue(true));!
        $pdo = $this-getMock('PDO', array ('prepare'), array ('sqlite::memory')); !
        $pdo-expects($this-once()) !
            -method('prepare') !
            -will($this-returnValue($pdostmt)); !

!

        $data = array ( !
            'code' = 'TST', !
            'title' = 'Test', !
            'description' = 'Testing Test', !
            'image' = 'http://www.example.com/image.png', !
            'price' = 10.00, !
            'created' = '2013-12-15 01:55:00', !
            'modified' = '2013-12-20 16:00:00', !
        ); !
        $product = new Product($data); !
        $product-setPdo($pdo); !
        $product-save(); !

!

        // The model has mentioning of productId !
        $data['productId'] = null; !
        $this-assertEquals($data, $product-toArray()); !
    }
57
Running	
  Data	
  Mocking

58
Running	
  Data	
  Mocking

58
Why	
  the	
  extra	
  work?
• Your	
  databases	
  are	
  fully	
  tested,	
  no	
  need	
  to	
  do	
  

it	
  yourself	
  again	
  
• The	
  connec6ons	
  are	
  expensive	
  and	
  delay	
  your	
  
tests	
  
• Your	
  tes6ng	
  code	
  that	
  needs	
  to	
  handle	
  the	
  data	
  
it	
  gets,	
  no	
  maQer	
  where	
  it	
  gets	
  it

59
Web	
  Services

60
Example:	
  joind.in

61
Joindin	
  Case:	
  talks.feryn.eu

Fork	
  it:	
  hQps://github.com/ThijsFeryn/talks.feryn.eu
62
API	
  Docs	
  are	
  your	
  friend

63
Joindin	
  Test
?php !
class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase !
{ !
    protected $_joindin; !
    protected $_settings; !
     !
    protected function setUp() !
    { !
        $this-_joindin = new Zftest_Service_Joindin(); !
        $settings = simplexml_load_file(realpath( !
            APPLICATION_PATH . '/../tests/_files/settings.xml')); !
        $this-_settings = $settings-joindin; !
        parent::setUp(); !
    } !
    protected function tearDown() !
    { !
        parent::tearDown(); !
        $this-_joindin = null; !
    } !
}

64
Joindin	
  Test	
  (2)
public function testJoindinCanGetUserDetails() !
{ !
    $expected = '?xml version=1.0?responseitemusernameDragonBe/
usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/
last_login/item/response'; !
    $this-_joindin-setUsername($this-_settings-username) !
                   -setPassword($this-_settings-password); !
    $actual = $this-_joindin-user()-getDetail(); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
} !

!

public function testJoindinCanCheckStatus() !
{ !
    $date = new DateTime(); !
    $date-setTimezone(new DateTimeZone('UTC')); !
    $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/
dttest_stringtesting unit test/test_string/response'; !
    $actual = $this-_joindin-site()-getStatus('testing unit test'); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
}

65
Running	
  the	
  test

66
Running	
  the	
  test

66
Euh…	
  what	
  just	
  happened?
1) Zftest_Service_JoindinTest::testJoindinCanGetUserDetails
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
ID19/ID
last_login1303248639/last_login
+
last_login1303250271/last_login
/item
/response

67
And	
  this?
2) Zftest_Service_JoindinTest::testJoindinCanCheckStatus
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
?xml version=1.0?
response
- dtTue, 19 Apr 2011 22:26:40 +0000/dt
+ dtTue, 19 Apr 2011 22:26:41 +0000/dt
test_stringtesting unit test/test_string
/response

68
No	
  dispair,	
  we	
  help	
  you	
  out!

69
Let’s	
  mock	
  the	
  HTTP	
  client
?php !
class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase !
{ !
    protected $_joindin; !
    protected $_settings; !
     !
    protected function setUp() !
    { !
        $this-_joindin = new Zftest_Service_Joindin(); !
        $client = new Zend_Http_Client(); !
        $client-setAdapter(new Zend_Http_Client_Adapter_Test()); !
        $this-_joindin-setClient($client); !
        $settings = simplexml_load_file(realpath( !
            APPLICATION_PATH . '/../tests/_files/settings.xml')); !
        $this-_settings = $settings-joindin; !
        parent::setUp(); !
    } !
    protected function tearDown() !
    { !
        parent::tearDown(); !
        $this-_joindin = null; !
    } !
}

70
Let’s	
  mock	
  the	
  HTTP	
  client
?php !
class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase !
{ !
    protected $_joindin; !
    protected $_settings; !
     !
    protected function setUp() !
    { !
        $this-_joindin = new Zftest_Service_Joindin(); !
        $client = new Zend_Http_Client(); !
$client-setAdapter(new Zend_Http_Client_Adapter_Test());
        $client-setAdapter(new Zend_Http_Client_Adapter_Test()); !
$this-_joindin-setClient($client); !
        $this-_joindin-setClient($client); !
        $settings = simplexml_load_file(realpath( !
$settings = simplexml_load_file(realpath( !
            APPLICATION_PATH . '/../tests/_files/settings.xml')); !
APPLICATION_PATH . '/../tests/_files/settings.xml'));
        $this-_settings = $settings-joindin; !
        parent::setUp(); !
    } !
    protected function tearDown() !
    { !
        parent::tearDown(); !
        $this-_joindin = null; !
    } !
}

!

70
Mocking	
  the	
  response
public function testJoindinCanGetUserDetails() !
{ !
    $response = EOS !
HTTP/1.1 200 OK !
Content-type: text/xml !

!

?xml version=1.0? !
response !
  item !
    usernameDragonBe/username !
    full_nameMichelangelo van Dam/full_name !
    ID19/ID !
    last_login1303248639/last_login !
  /item !
/response         !
EOS; !
    $client = $this-_joindin-getClient()-getAdapter()-setResponse($response); !
    $expected = '?xml version=1.0?responseitemusernameDragonBe/
usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/
last_login/item/response'; !
    $this-_joindin-setUsername($this-_settings-username) !
                   -setPassword($this-_settings-password); !
    $actual = $this-_joindin-user()-getDetail(); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
}

71
Mocking	
  the	
  response
public function testJoindinCanGetUserDetails() !
{ !
    $response = EOS !
HTTP/1.1 200 OK !
Content-type: text/xml !

!

?xml version=1.0? !
response !
  item !
    usernameDragonBe/username !
    full_nameMichelangelo van Dam/full_name !
    ID19/ID !
    last_login1303248639/last_login !
  /item !
/response         !
EOS; !
$client = $this-_joindin-getClient()-getAdapter()-setResponse($response);
    $client = $this-_joindin-getClient()-getAdapter()-setResponse($response); !
    $expected = '?xml version=1.0?responseitemusernameDragonBe/
usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/
last_login/item/response'; !
    $this-_joindin-setUsername($this-_settings-username) !
                   -setPassword($this-_settings-password); !
    $actual = $this-_joindin-user()-getDetail(); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
}

71
Same	
  here
public function testJoindinCanCheckStatus() !
{ !
    $date = new DateTime(); !
    $date-setTimezone(new DateTimeZone('UTC')); !
    $response = EOS !
HTTP/1.1 200 OK !
Content-type: text/xml !

!

?xml version=1.0? !
response !
  dt{$date-format('r')}/dt !
  test_stringtesting unit test/test_string !
/response         !
EOS; !

!

    $client = $this-_joindin-getClient() !
                             -getAdapter()-setResponse($response);!
!
    $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/
dttest_stringtesting unit test/test_string/response'; !
    $actual = $this-_joindin-site()-getStatus('testing unit test'); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
}

72
Same	
  here
public function testJoindinCanCheckStatus() !
{ !
    $date = new DateTime(); !
    $date-setTimezone(new DateTimeZone('UTC')); !
    $response = EOS !
HTTP/1.1 200 OK !
Content-type: text/xml !

!

?xml version=1.0? !
response !
  dt{$date-format('r')}/dt !
  test_stringtesting unit test/test_string !
/response         !
EOS; !

!

$client = $this-_joindin-getClient() !
    $client = $this-_joindin-getClient() !
                         -getAdapter()-setResponse($response);
                             -getAdapter()-setResponse($response);!
!
    $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/
dttest_stringtesting unit test/test_string/response'; !
    $actual = $this-_joindin-site()-getStatus('testing unit test'); !
    $this-assertXmlStringEqualsXmlString($expected, $actual); !
}

72
Now	
  we’re	
  good

73
Now	
  we’re	
  good

73
Conclusion

74
Tes6ng	
  is	
  easy

75
Even	
  for	
  spaghen	
  code

76
Posi6ve	
  	
  Nega6ve	
  tests

77
Doing	
  it	
  more,	
  makes	
  you	
  beQer

78
Recommended	
  reading
Click on the images to view

www.owasp.org

planet.phpunit.de
79
#PHPBNL14
January 25 - 26, 2014

phpcon.eu

80
https://joind.in/10113
If you liked it, thank you!	

If not, tell me how to improve this talk
81
Michelangelo van Dam
Zend Certified Engineer	

!

michelangelo@in2it.be

PHP Consulting - QA audits - Training	

!

www.in2it.be
82
Credits
• Me:	
  hQp://www.flickr.com/photos/akrabat/8784318813	
  
• CrashTest:	
  hQp://www.flickr.com/photos/digi6zedchaos/3964206549	
  
• Chris:	
  hQp://www.flickr.com/photos/akrabat/8421560178	
  
• Nike:	
  hQp://www.flickr.com/photos/japokskee/4393860599	
  
• Grads:	
  hQp://www.flickr.com/photos/ajschwegler/525829339	
  
• Econopoly:	
  hQp://www.flickr.com/photos/danielbroche/2258988806	
  
• Disaster:	
  hQp://www.flickr.com/photos/eschipul/1484495808/	
  
• Mountain:	
  hQp://www.flickr.com/photos/jfdervin/2510535266	
  
• Data	
  Store:	
  hQp://www.flickr.com/photos/comedynose/7048321621	
  
• Protect:	
  hQp://www.flickr.com/photos/boltowlue/5724934828	
  
• Owl:	
  hQp://www.flickr.com/photos/15016964@N02/9425608812	
  
• Register:	
  hQp://www.flickr.com/photos/taedc/5466788868	
  
• Crying	
  Baby:	
  hQp://www.flickr.com/photos/bibbit/5456802728	
  
• Smiling	
  Donkey:	
  hQp://www.flickr.com/photos/smkybear/2239030703	
  
• Jump	
  high:	
  hQp://www.flickr.com/photos/96748294@N06/9356699040	
  
• Chipmunk:	
  hQp://www.flickr.com/photos/exfordy/1184487050	
  
• Easy:	
  hQp://www.flickr.com/photos/dalismustaches/223972376	
  
• Spaghen:	
  hQp://www.flickr.com/photos/lablasco/5512066970	
  
• BaQery:	
  hQp://www.flickr.com/photos/shalf/6088539194	
  
• Elephpant:	
  hQp://www.flickr.com/photos/dragonbe/11403208686
83
Thank	
  you

84

More Related Content

What's hot

Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit Testing
Mike Lively
 
Phpunit testing
Phpunit testingPhpunit testing
Phpunit testing
Nikunj Bhatnagar
 
PhpUnit Best Practices
PhpUnit Best PracticesPhpUnit Best Practices
PhpUnit Best Practices
Edorian
 
PHPUnit
PHPUnitPHPUnit
Test your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practiceTest your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practice
Sebastian Marek
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
Michelangelo van Dam
 
UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013
Michelangelo van Dam
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnitJace Ju
 
Unit Testng with PHP Unit - A Step by Step Training
Unit Testng with PHP Unit - A Step by Step TrainingUnit Testng with PHP Unit - A Step by Step Training
Unit Testng with PHP Unit - A Step by Step Training
Ram Awadh Prasad, PMP
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
mfrost503
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
chartjes
 
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEnterprise PHP Center
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytest
Hector Canto
 
Unit testing
Unit testingUnit testing
Unit testing
Arthur Purnama
 
Keep your repo clean
Keep your repo cleanKeep your repo clean
Keep your repo clean
Hector Canto
 
Unlock The Mystery Of PHPUnit (Wave PHP 2018)
Unlock The Mystery Of PHPUnit (Wave PHP 2018)Unlock The Mystery Of PHPUnit (Wave PHP 2018)
Unlock The Mystery Of PHPUnit (Wave PHP 2018)
ENDelt260
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2Yi-Huan Chan
 
PHPUnit testing to Zend_Test
PHPUnit testing to Zend_TestPHPUnit testing to Zend_Test
PHPUnit testing to Zend_Test
Michelangelo van Dam
 
Python testing using mock and pytest
Python testing using mock and pytestPython testing using mock and pytest
Python testing using mock and pytest
Suraj Deshmukh
 

What's hot (20)

Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit Testing
 
Phpunit testing
Phpunit testingPhpunit testing
Phpunit testing
 
PhpUnit Best Practices
PhpUnit Best PracticesPhpUnit Best Practices
PhpUnit Best Practices
 
PHPUnit
PHPUnitPHPUnit
PHPUnit
 
Test your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practiceTest your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practice
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnit
 
Unit Testng with PHP Unit - A Step by Step Training
Unit Testng with PHP Unit - A Step by Step TrainingUnit Testng with PHP Unit - A Step by Step Training
Unit Testng with PHP Unit - A Step by Step Training
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur PurnamaEPHPC Webinar Slides: Unit Testing by Arthur Purnama
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytest
 
Unit testing
Unit testingUnit testing
Unit testing
 
Keep your repo clean
Keep your repo cleanKeep your repo clean
Keep your repo clean
 
Unlock The Mystery Of PHPUnit (Wave PHP 2018)
Unlock The Mystery Of PHPUnit (Wave PHP 2018)Unlock The Mystery Of PHPUnit (Wave PHP 2018)
Unlock The Mystery Of PHPUnit (Wave PHP 2018)
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2
 
PHPUnit testing to Zend_Test
PHPUnit testing to Zend_TestPHPUnit testing to Zend_Test
PHPUnit testing to Zend_Test
 
Laravel Unit Testing
Laravel Unit TestingLaravel Unit Testing
Laravel Unit Testing
 
Python testing using mock and pytest
Python testing using mock and pytestPython testing using mock and pytest
Python testing using mock and pytest
 

Viewers also liked

CakePHP Grandes Empresas
CakePHP Grandes EmpresasCakePHP Grandes Empresas
CakePHP Grandes Empresas
Fabian Andres Ramirez Sepulveda
 
Integracion Cakephp y Bootstrap
Integracion Cakephp y BootstrapIntegracion Cakephp y Bootstrap
Integracion Cakephp y Bootstrap
Deltron
 
Herramientas Web 1.0,2.0 Y 3.0
Herramientas Web 1.0,2.0 Y 3.0Herramientas Web 1.0,2.0 Y 3.0
Herramientas Web 1.0,2.0 Y 3.0
Daniel Leffa
 
Iniciación Con CakePHP
Iniciación Con CakePHPIniciación Con CakePHP
Iniciación Con CakePHP
Victor San Martin
 
Continuous delivery met jenkins twist en puppet
Continuous delivery met jenkins twist en puppetContinuous delivery met jenkins twist en puppet
Continuous delivery met jenkins twist en puppet
ltebbens
 
Continuous deployment 2.0
Continuous deployment 2.0Continuous deployment 2.0
Continuous deployment 2.0
Michelangelo van Dam
 
Continuous Quality Assurance
Continuous Quality AssuranceContinuous Quality Assurance
Continuous Quality Assurance
Michelangelo van Dam
 

Viewers also liked (8)

CakePHP Grandes Empresas
CakePHP Grandes EmpresasCakePHP Grandes Empresas
CakePHP Grandes Empresas
 
Integracion Cakephp y Bootstrap
Integracion Cakephp y BootstrapIntegracion Cakephp y Bootstrap
Integracion Cakephp y Bootstrap
 
Herramientas Web 1.0,2.0 Y 3.0
Herramientas Web 1.0,2.0 Y 3.0Herramientas Web 1.0,2.0 Y 3.0
Herramientas Web 1.0,2.0 Y 3.0
 
Iniciación Con CakePHP
Iniciación Con CakePHPIniciación Con CakePHP
Iniciación Con CakePHP
 
Especiacion2
Especiacion2Especiacion2
Especiacion2
 
Continuous delivery met jenkins twist en puppet
Continuous delivery met jenkins twist en puppetContinuous delivery met jenkins twist en puppet
Continuous delivery met jenkins twist en puppet
 
Continuous deployment 2.0
Continuous deployment 2.0Continuous deployment 2.0
Continuous deployment 2.0
 
Continuous Quality Assurance
Continuous Quality AssuranceContinuous Quality Assurance
Continuous Quality Assurance
 

Similar to Unit testing PHP apps with PHPUnit

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
Mark Niebergall
 
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
LB Denker
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
Michelangelo van Dam
 
Test Driven Development with JavaFX
Test Driven Development with JavaFXTest Driven Development with JavaFX
Test Driven Development with JavaFX
Hendrik Ebbers
 
2009-02 Oops!
2009-02 Oops!2009-02 Oops!
2009-02 Oops!
terry chay
 
Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPFabien Potencier
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
ciklum_ods
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Michelangelo van Dam
 
Tango with django
Tango with djangoTango with django
Tango with django
Rajan Kumar Upadhyay
 
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
Mark Niebergall
 
Continuous integration with Git & CI Joe
Continuous integration with Git & CI JoeContinuous integration with Git & CI Joe
Continuous integration with Git & CI JoeShawn Price
 
Milot Shala - C++ (OSCAL2014)
Milot Shala - C++ (OSCAL2014)Milot Shala - C++ (OSCAL2014)
Milot Shala - C++ (OSCAL2014)
Open Labs Albania
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineering
Miro Wengner
 
New Ideas for Old Code - Greach
New Ideas for Old Code - GreachNew Ideas for Old Code - Greach
New Ideas for Old Code - Greach
HamletDRC
 
Building Potent WordPress Websites
Building Potent WordPress WebsitesBuilding Potent WordPress Websites
Building Potent WordPress Websites
Kyle Cearley
 
Workshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublinWorkshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublin
Michelangelo van Dam
 
Extracting Plugins And Gems From Rails Apps
Extracting Plugins And Gems From Rails AppsExtracting Plugins And Gems From Rails Apps
Extracting Plugins And Gems From Rails Apps
Josh Nichols
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartGabriele Lana
 
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
 
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
 

Similar to Unit testing PHP apps with PHPUnit (20)

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
 
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
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Test Driven Development with JavaFX
Test Driven Development with JavaFXTest Driven Development with JavaFX
Test Driven Development with JavaFX
 
2009-02 Oops!
2009-02 Oops!2009-02 Oops!
2009-02 Oops!
 
Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHP
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
 
Tango with django
Tango with djangoTango with django
Tango with django
 
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
 
Continuous integration with Git & CI Joe
Continuous integration with Git & CI JoeContinuous integration with Git & CI Joe
Continuous integration with Git & CI Joe
 
Milot Shala - C++ (OSCAL2014)
Milot Shala - C++ (OSCAL2014)Milot Shala - C++ (OSCAL2014)
Milot Shala - C++ (OSCAL2014)
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineering
 
New Ideas for Old Code - Greach
New Ideas for Old Code - GreachNew Ideas for Old Code - Greach
New Ideas for Old Code - Greach
 
Building Potent WordPress Websites
Building Potent WordPress WebsitesBuilding Potent WordPress Websites
Building Potent WordPress Websites
 
Workshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublinWorkshop quality assurance for php projects - phpdublin
Workshop quality assurance for php projects - phpdublin
 
Extracting Plugins And Gems From Rails Apps
Extracting Plugins And Gems From Rails AppsExtracting Plugins And Gems From Rails Apps
Extracting Plugins And Gems From Rails Apps
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing Part
 
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?"
 
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?
 

More from Michelangelo van Dam

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and default
Michelangelo van Dam
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functions
Michelangelo van Dam
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
Michelangelo van Dam
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
Michelangelo van Dam
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
Michelangelo van Dam
 
Let your tests drive your code
Let your tests drive your codeLet your tests drive your code
Let your tests drive your code
Michelangelo van Dam
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's story
Michelangelo van Dam
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantage
Michelangelo van Dam
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
Michelangelo van Dam
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
Michelangelo van Dam
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me later
Michelangelo van Dam
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutes
Michelangelo van Dam
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heaven
Michelangelo van Dam
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
Michelangelo van Dam
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your projectMichelangelo van Dam
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
Michelangelo van Dam
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
Michelangelo van Dam
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
Michelangelo van Dam
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an api
Michelangelo van Dam
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
Michelangelo van Dam
 

More from Michelangelo van Dam (20)

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and default
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functions
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
Let your tests drive your code
Let your tests drive your codeLet your tests drive your code
Let your tests drive your code
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's story
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantage
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me later
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutes
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heaven
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an api
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
 

Recently uploaded

PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
Ralf Eggert
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
nkrafacyberclub
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
Peter Spielvogel
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
Matthew Sinclair
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
Neo4j
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
SOFTTECHHUB
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
James Anderson
 

Recently uploaded (20)

PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
Why You Should Replace Windows 11 with Nitrux Linux 3.5.0 for enhanced perfor...
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
 

Unit testing PHP apps with PHPUnit

  • 1. Day Camp 4 Developers Day Camp 4 Developers & 2 Unit Testing PHP apps with PHPUnit
  • 4. Excuses  not  to  test • No  6me   • Not  in  budget   • We  don’t  know  how     -­‐ valid  and  honest  answer   -­‐ right,  like  that’s  going  to  happen   • We  add  unit  tests  a@er  finish  project   •… 4
  • 6. Unit  tes6ng  is  fun  and  easy! • When  you  write  code   • You  already  test  in  your  head   • Write  out  these  test(s)   • And  protect  your  code  base 6
  • 7. How  to  get  started? 7
  • 8. My  example  code • Get  this  example  from  GitHub   -­‐ hQps://github.com/in2it/Utexamples project/ src/ Utexamples/ Calculator.php autoload.php tests/ Utexamples/ CalculatorTest.php 8
  • 9. Simple  example  class • Add  by  one   -­‐ adds  the  current  value  by  one ?php ! namespace Utexamples; ! ! /** !  * Class that allows us to make all sorts of calculations !  */ ! class Calculator ! { !     protected $_value = 0; ! !     /** !      * Adds the current value by one !      * !      * @return int The value from this method !      */ !     public function addByOne() !     { !         $this-_value++; !         return $this-_value; !     } ! } 9
  • 10. Our  unit  test ?php ! namespace Utexamples; ! ! Class CalculatorTest extends PHPUnit_Framework_TestCase ! { !     public function testCalculatorCanAddByOne() !     { !         $calculator = new Calculator(); !         $result = $calculator-addByOne(); !         $this-assertSame(1, $result); !     } ! } 10
  • 11. My  autoloader ?php ! ! /** !  * Simple autoloader that follow the PHP Standards Recommendation #0 (PSR-0) !  * @see https://github.com/php-fig/fig-standards/blob/master/accepted/ PSR-0.md for more informations. !  * !  * Code inspired from the SplClassLoader RFC !  * @see https://wiki.php.net/rfc/splclassloader#example_implementation !  */ ! spl_autoload_register(function($className) { !     $className = ltrim($className, ''); !     $fileName = ''; !     $namespace = ''; !     if ($lastNsPos = strripos($className, '')) { !         $namespace = substr($className, 0, $lastNsPos); !         $className = substr($className, $lastNsPos + 1); !         $fileName = str_replace(! '', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; !     } !     $fileName = __DIR__ . DIRECTORY_SEPARATOR . $fileName . $className . '.php'; !     if (file_exists($fileName)) { !         require $fileName; ! !         return true; !     } ! !     return false; ! }); 11
  • 14. A  lot  of  parameters! • Easily  possible  to  make  mistakes   • Every  developer  might  use  different  params   • Not  easy  for  (semi-­‐)automated  tes6ng   -­‐ Using  IDE  for  running  unit  tests 13
  • 15. Let’s  op6mise  this ?xml version=1.0 encoding=UTF-8? ! !-- file: project/phpunit.xml -- ! phpunit bootstrap=./src/autoload.php colors=true ! testsuite name=Unit Test Example code directory./tests/directory /testsuite ! filter whitelist directory suffix=.php./src/directory exclude directory suffix=.phtml./src/directory /exclude /whitelist /filter ! /phpunit 14
  • 16. Now  run  more  relaxed 15
  • 17. Now  run  more  relaxed 15
  • 18. We’re  no  grads  anymore 16
  • 19. Real  apps,  real  money 17
  • 20. 18
  • 21. How  to  reach  the  top 19
  • 23. Simple  Product  Model Product _productId : integer _code : string _title : string _description : string _image : string _price : float _created : DateTime _modified : DateTime __construct($params : null | array) setProductId($productId : integer) : Product getProductId() : integer setCode($code : string) : Product getCode() : string setTitle($title : string) : Product getTitle() : string setDescription($description : string) : Product getDescription() : string setImage($image : string) : Product getImage() : string setPrice($price : float) : Product getPrice() : float setCreated($created : string | DateTime) : Product getCreated() : DateTime setModified($modified : string | DateTime) : Product getModified() : DateTime populate($data : array) toArray() : array __toString() : string 21
  • 24. A  simple  ProductTest ?php ! namespace UtexamplesModel; ! /** !  * Class ProductTest !  * @package UtexamplesModel !  * @group Model !  */ ! class ProductTest extends PHPUnit_Framework_TestCase ! { !     public function testProductCanBePopulated() !     { !         $data = array ( !             'productId' = 1, !             'code' = 'TSTPROD1', !             'title' = 'Test product 1', !             'description' = 'This is a full description of test product 1', !             'image' = 'image.png', !             'price' = 123.95, !             'created' = '2013-11-20 16:00:00', !             'modified' = '2013-11-20 17:00:00', !         ); ! !         $product = new Product($data); !         $this-assertEquals($data, $product-toArray()); !     } ! } 22
  • 25. A  simple  ProductTest ?php ! namespace UtexamplesModel; ! /** !  * Class ProductTest !  * @package UtexamplesModel !  * @group Model !  */ ! class ProductTest extends PHPUnit_Framework_TestCase ! { !     public function testProductCanBePopulated() !     { !         $data = array ( !             'productId' = 1, !             'code' = 'TSTPROD1', !             'title' = 'Test product 1', !             'description' = 'This is a full description of test product 1', !             'image' = 'image.png', !             'price' = 123.95, !             'created' = '2013-11-20 16:00:00', !             'modified' = '2013-11-20 17:00:00', !         ); ! !         $product = new Product($data); !         $product = new Product($data); !         $this-assertEquals($data, $product-toArray());         $this-assertEquals($data, $product-toArray()); !     } ! } 22
  • 28. data  fixture     public function goodDataProvider() { !         return array ( !             array ( !                 1, !                 'TSTPROD1', !                 'Test Product 1', !                 'This is a full description of test product 1', !                 'image.png', !                 123.95, !                 '2013-11-20 16:00:00', !                 '2013-11-20 17:00:00', !             ), !             array ( !                 2, !                 'TSTPROD2', !                 'Test Product 2', !                 'This is a full description of test product 2', !                 'image.png', !                 4125.99, !                 '2013-11-20 16:00:00', !                 '2013-11-20 17:00:00', !             ), !         ); !     } 24
  • 29. Using  @dataProvider     /** !      * @dataProvider goodDataProvider !      */ !     public function testProductCanBePopulated( !         $productId, $code, $title, $description, $image, $price, $created, $modified !     ) !     { !         $data = array ( !             'productId' = $productId, !             'code' = $code, !             'title' = $title, !             'description' = $description, !             'image' = $image, !             'price' = $price, !             'created' = $created, !             'modified' = $modified, !         ); ! !         $product = new Product($data); !         $this-assertEquals($data, $product-toArray()); !     } 25
  • 30. Using  @dataProvider     /** !     /** !      * @dataProvider goodDataProvider !      * @dataProvider goodDataProvider !      */ !      */     public function testProductCanBePopulated( !         $productId, $code, $title, $description, $image, $price, $created, $modified !     ) !     { !         $data = array ( !             'productId' = $productId, !             'code' = $code, !             'title' = $title, !             'description' = $description, !             'image' = $image, !             'price' = $price, !             'created' = $created, !             'modified' = $modified, !         ); ! !         $product = new Product($data); !         $this-assertEquals($data, $product-toArray()); !     } 25
  • 33. To  protect  and  to  serve 27
  • 34. OWASP  top  10  exploits https://www.owasp.org/index.php/Top_10_2013-Top_10 28
  • 36. Libs  you  can  use • Zend  Framework  1:  Zend_Filter_Input   • Zend  Framework  2:  ZendInputFilter   • Symfony:  SymfonyComponentValidator   • Aura:  AuraFrameworkInputFilter   • Lithium:  lithiumu6lValidator   • Laravel:  AppValidator 30
  • 37. Modify  our  Product  class ?php ! namespace UtexamplesModel; ! ! class Product extends ModelAbstract ! { ! ...!     /** !      * @var Zend_Filter_Input The filter/validator for this Product !      */ !     protected $_inputFilter; ! !     /** !      * @var bool The validation of data for this Product !      */ !     protected $_valid; ! !     /** !      * Helper class to create filter and validation rules !      * !      * @access protected !      */ !     protected function _createInputFilter() !     { ! ...!     } ! ...! } 31
  • 38. _createInputFilter()     protected function _createInputFilter() !     { !         $filters = array ( !             'productId' = array('Int'), !             'code' = array ('StripTags', 'StringTrim', 'StringToUpper'), !             'title' = array ('StripTags', 'StringTrim'), !             'description' = array ('StripTags', 'StringTrim'), !             'image' = array ('StripTags', 'StringTrim','StringToLower'), !             'price' = array (), !         ); !         $validators = array ( !             'productId' = array ( !                 'Int', !                 array ('GreaterThan', array ('min' = 0, 'inclusive' = true)), !             ), !             'code' = array ( !                 'Alnum', !                 array ('StringLength', array ('min' = 5, 'max' = 50)), !             ), !             'title' = array ('NotEmpty'), !             'description' = array ('NotEmpty'), !             'image' = array ('NotEmpty'), !             'price' = array ( !                 'Float', !                 array ('GreaterThan', array ('min' = 0, 'inclusive' = true)), !             ), !         ); !         $this-_inputFilter = new Zend_Filter_Input($filters, $validators); !     } 32
  • 39. Modify  your  seQers     /** !      * Set the product code for this Product !      * !      * @param string $code !      * @return Product !      */ !     public function setCode($code) !     { !         $this-_inputFilter-setData(array ('code' = $code)); !         if ($this-_inputFilter-isValid('code')) { !             $this-_code = $this-_inputFilter-code; !             $this-setValid(true); !         } else { !             $this-setValid(false); !         } !         return $this; !     } 33
  • 40. Modify  your  seQers     /** !      * Set the product code for this Product !      * !      * @param string $code !      * @return Product !      */ !     public function setCode($code) !         $this-_inputFilter-setData(array ('code' = $code));     { !         if ($this-_inputFilter-isValid('code')) { !         $this-_inputFilter-setData(array ('code' = $code)); !             $this-_code = $this-_inputFilter-code; !         if ($this-_inputFilter-isValid('code')) { !             $this-_code = $this-_inputFilter-code; !             $this-setValid(true); !             $this-setValid(true); !         } else { !         } else { !             $this-setValid(false); !             $this-setValid(false); !         } ! !         }         return $this; !         return $this;     } ! 33
  • 41. Using  dataproviders  again     public function badDataProvider() !     { !         return array ( !             array ( !                 1, '', '', '', '', 0, !                 '0000-00-00 00:00:00', !                 '0000-00-00 00:00:00', !             ), !             array ( !                 1, !                 '!@#$%^@^*{}[]=-/'', 'Test Product 1', !                 'This is a full description of test product 1', 'image.png', !                 123.95, '2013-11-20 16:00:00', '2013-11-20 17:00:00', !             ), !             array ( !                 1, '' OR 1=1; --', 'Test Product 1', !                 'This is a full description of test product 1', 'image.png', !                 123.95, '2013-11-20 16:00:00', '2013-11-20 17:00:00', !             ), !         ); !     } 34
  • 42. And  now  we  test  for  valid  data     /** !      * @dataProvider badDataProvider !      */ !     public function testProductRejectsBadData( !         $productId, $code, $title, $description, $image, $price, $created, $modified !     ) !     { !         $data = array ( !             'productId' = $productId, !             'code' = $code, !             'title' = $title, !             'description' = $description, !             'image' = $image, !             'price' = $price, !             'created' = $created, !             'modified' = $modified, !         ); ! !         $product = new Product($data); !         $this-assertFalse($product-isValid()); !     } 35
  • 43. And  now  we  test  for  valid  data     /** !      * @dataProvider badDataProvider !      */ !     public function testProductRejectsBadData( !         $productId, $code, $title, $description, $image, $price, $created, $modified !     ) !     { !         $data = array ( !             'productId' = $productId, !             'code' = $code, !             'title' = $title, !             'description' = $description, !             'image' = $image, !             'price' = $price, !             'created' = $created, !             'modified' = $modified, !         ); ! !         $product = new Product($data); !         $product = new Product($data); !         $this-assertFalse($product-isValid()); !         $this-assertFalse($product-isValid());     } 35
  • 46. Databases,  the  wisdom  fountain 37
  • 47. 4  stages  of  database  tes6ng • Setup  table  fixtures   • Run  tests  on  the  database  interac6ons   • Verify  results  of  tests   • Teardown  the  fixtures 38
  • 49. Hello  DBUnit ?php ! namespace UtexamplesModel; ! ! use PHPUnit_Extensions_Database_DataSet_IDataSet; ! use PHPUnit_Extensions_Database_DB_IDatabaseConnection; ! use PHPUnit_Extensions_Database_DataSet_QueryDataSet; ! ! class ProductDbTest extends PHPUnit_Extensions_Database_TestCase ! { !     protected $_pdo; ! !     public function __construct() !     { !         $this-_pdo = new PDO('sqlite::memory:'); !         $this-_pdo-exec(! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')! ); !     } ! !     final public function getConnection() !     { !         return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !     } ! !     protected function getDataSet() !     { !         return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !     } ! } 40
  • 50. Hello  DBUnit ?php ! namespace UtexamplesModel; ! ! use PHPUnit_Extensions_Database_DataSet_IDataSet; ! use PHPUnit_Extensions_Database_DB_IDatabaseConnection; ! use PHPUnit_Extensions_Database_DataSet_QueryDataSet; ! ! class ProductDbTest extends PHPUnit_Extensions_Database_TestCase ! { !     protected $_pdo; ! protected $_pdo; ! ! !     public function __construct() !     { ! public function __construct() !         $this-_pdo = new PDO('sqlite::memory:'); ! { !         $this-_pdo-exec(!     $this-_pdo = new PDO('sqlite::memory:'); ! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')!     $this-_pdo-exec(! ); !     } ! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')! ! ); !     final public function getConnection() ! }     { !         return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !     } ! !     protected function getDataSet() !     { !         return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !     } ! } 40
  • 51. Hello  DBUnit ?php ! namespace UtexamplesModel; ! ! use PHPUnit_Extensions_Database_DataSet_IDataSet; ! use PHPUnit_Extensions_Database_DB_IDatabaseConnection; ! use PHPUnit_Extensions_Database_DataSet_QueryDataSet; ! ! class ProductDbTest extends PHPUnit_Extensions_Database_TestCase ! { !     protected $_pdo; ! !     public function __construct() !     { !         $this-_pdo = new PDO('sqlite::memory:'); !         $this-_pdo-exec(! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')! ); ! final public function getConnection() !     } ! { ! !     return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !     final public function getConnection() !     { ! }         return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); !     } ! !     protected function getDataSet() !     { !         return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); !     } ! } 40
  • 52. Hello  DBUnit ?php ! namespace UtexamplesModel; ! ! use PHPUnit_Extensions_Database_DataSet_IDataSet; ! use PHPUnit_Extensions_Database_DB_IDatabaseConnection; ! use PHPUnit_Extensions_Database_DataSet_QueryDataSet; ! ! class ProductDbTest extends PHPUnit_Extensions_Database_TestCase ! { !     protected $_pdo; ! !     public function __construct() !     { !         $this-_pdo = new PDO('sqlite::memory:'); !         $this-_pdo-exec(! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')! ); !     } ! !     final public function getConnection() !     { ! protected function getDataSet() !         return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); ! { !     } !     return $this-createFlatXMLDataSet(! ! dirname(__DIR__) . ‘/_files/initialDataSet.xml'!     protected function getDataSet() !     {); ! !         return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); ! }     } ! } 40
  • 53. Hello  DBUnit ?php ! namespace UtexamplesModel; ! ! use PHPUnit_Extensions_Database_DataSet_IDataSet; ! use PHPUnit_Extensions_Database_DB_IDatabaseConnection; ! use PHPUnit_Extensions_Database_DataSet_QueryDataSet; ! ! class ProductDbTest extends PHPUnit_Extensions_Database_TestCase ! { !     protected $_pdo; ! !     public function __construct() !     { !         $this-_pdo = new PDO('sqlite::memory:'); !         $this-_pdo-exec(! file_get_contents(dirname(__DIR__) . ‘/../../data/schema.sqlite.sql')! ); !     } ! !     final public function getConnection() !     { ! protected function getDataSet() !         return $this-createDefaultDBConnection($this-_pdo, 'sqlite'); ! { !     } !     return $this-createFlatXMLDataSet(! ! dirname(__DIR__) . ‘/_files/initialDataSet.xml'!     protected function getDataSet() !     {); ! !         return $this-createFlatXMLDataSet(dirname(__DIR__) . '/_files/initialDataSet.xml'); ! }     } ! } 40
  • 54. Ini6alDataset ?xml version=1.0 encoding=UTF-8? dataset product productId=1 code=TEST CODE1 title=First test product description=This is our first test product image=http://www.example.com/image/image.png price=150.95 created=2013-03-30 10:11:12 modified=2013-12-11 09:08:07/ product productId=2 code=TEST CODE2 title=Second test product description=This is our second test product image=http://www.example.com/image/image.png price=19999.00 created=2013-03-30 10:11:12 modified=2013-12-11 09:08:07/ product productId=3 code=TEST CODE3 title=Third test product description=This is our third test product image=http://www.example.com/image/image.png price=0.45 created=2013-03-30 10:11:12 modified=2013-12-11 09:08:07/ /dataset 41
  • 55. First  DB  Test     public function testProductsCanBeLoadedFromDatabase() !     { !         $currentDataset = $this-getDataSet(); ! !         $expectedDataset = $this-createFlatXmlDataSet( !             dirname(__DIR__) . '/_files/selectDataSet.xml' !         ); ! !         $this-assertDataSetsEqual($expectedDataset, $currentDataset); !     } 42
  • 56. Adding  Data  Test     public function testProductAddToDatabase() !     { !         $data = array ( !             'code' = 'TST', !             'title' = 'Test', !             'description' = 'Testing Test', !             'image' = 'http://www.example.com/image.png', !             'price' = 10.00, !             'created' = '2013-12-15 01:55:00', !             'modified' = '2013-12-20 16:00:00', !         ); ! !         $product = new Product($data); !         $product-setPdo($this-_pdo); !         $product-save(); ! !         $expectedDs = $this-createFlatXMLDataSet( !             dirname(__DIR__) . '/_files/addProductDataSet.xml' !         ); !         $currentDs = $this-getConnection()-createDataSet(array ('product')); !         $this-assertDataSetsEqual($expectedDs, $currentDs); !     } 43
  • 57. addProductDataSet.xml ?xml version=1.0 encoding=UTF-8? dataset ... product productId=4 code=TEST title=Test description=Testing Test image=http://www.example.com/image.png price=10.0 created=2013-12-15 01:55:00 modified=2013-12-20 16:00:00”/ /dataset 44
  • 63. Oh  no,  I  made  a  TYPO! 47
  • 64. addProductDataSet.xml ?xml version=1.0 encoding=UTF-8? dataset ... product productId=4 code=TST title=Test description=Testing Test image=http://www.example.com/image.png price=10.0 created=2013-12-15 01:55:00 $data = array ( ! modified=2013-12-20 16:00:00”/     'code' = 'TST', ! /dataset     'title' = 'Test', !     'description' = 'Testing Test', !     'image' = 'http://www.example.com/image.png', !     'price' = 10.00, !     'created' = '2013-12-15 01:55:00', !     'modified' = '2013-12-20 16:00:00', ! ); 48
  • 68. Some  downsides • Tes6ng  databases  takes  6me   -­‐ -­‐ -­‐ -­‐ -­‐ create  a  real  database  connec6on   reini6alise  the  database  (load  schema,  truncate  tables)   load  ini6al  state  before  test  (with  each  test)   execute  on  the  database   compare  expected  result  with  actual  result 51
  • 69. We  can  do  beQer! 52
  • 70. Mock  objects • They  replace  an  object  for  tes6ng   -­‐ -­‐ a  class  with  all  methods   a  class  with  a  single  method   -­‐ -­‐ -­‐ databases   web  services   file  systems   -­‐ once  you  got  everything  set  up • Since  they  replace  “expansive”  connec6ons   • Are  quicker  and  more  reliable  to  test   53
  • 71. Same  tests,  but  now  mocked ?php ! ! namespace UtexamplesModel; ! ! use PDO; ! use PDOStatement; ! ! class ProductMockTest extends PHPUnit_Framework_TestCase ! { !     public function testProductsCanBeLoadedFromDatabase() !     { ! !     } ! !     public function testProductAddToDatabase() !     { ! !     } ! } 54
  • 72. testProductsCanBeLoadedFromDatabase     public function testProductsCanBeLoadedFromDatabase() !     { !         $data = array (); !         // let's mock the prepare statement !         $pdostmt = $this-getMock('PDOStatement', array ('execute', 'fetchAll')); !         $pdostmt-expects($this-atLeastOnce()) !             -method('execute') !             -will($this-returnValue(true)); !         $pdostmt-expects($this-atLeastOnce()) !             -method('fetchAll') !             -will($this-returnValue($data)); !         // let's mock the PDO object and return the mocked statement !         $pdo = $this-getMock('PDO', array ('prepare'), array ('sqlite::memory')); !         $pdo-expects($this-atLeastOnce()) !             -method('prepare') !             -will($this-returnValue($pdostmt)); ! !         $productCollection = new ProductCollection(); !         $productCollection-setPdo($pdo); !         $productCollection-fetchAll(); ! !         $this-assertEquals($data, $productCollection-toArray()); !     } 55
  • 73. My  $data  array         $data = array ( !             array ( !                 'productId' = 1, !                 'code' = 'TST1', !                 'title' = 'Test 1', !                 'description' = 'Testing product 1', !                 'image' = 'http://www.example.com/image1.png', !                 'price' = 10.00, !                 'created' = '2013-12-01 01:55:00', !                 'modified' = '2013-12-20 16:00:00', !             ), !             array ( !                 'productId' = 2, !                 'code' = 'TST2', !                 'title' = 'Test 2', !                 'description' = 'Testing product 2', !                 'image' = 'http://www.example.com/image2.png', !                 'price' = 199.95, !                 'created' = '2013-12-02 02:55:00', !                 'modified' = '2013-12-20 16:00:00', !             ), !         ); 56
  • 74. testProductAddToDatabase     public function testProductAddToDatabase() !     { !         $pdostmt = $this-getMock('PDOStatement', array ('execute')); !         $pdostmt-expects($this-atLeastOnce()) !             -method('execute') !             -will($this-returnValue(true));!         $pdo = $this-getMock('PDO', array ('prepare'), array ('sqlite::memory')); !         $pdo-expects($this-once()) !             -method('prepare') !             -will($this-returnValue($pdostmt)); ! !         $data = array ( !             'code' = 'TST', !             'title' = 'Test', !             'description' = 'Testing Test', !             'image' = 'http://www.example.com/image.png', !             'price' = 10.00, !             'created' = '2013-12-15 01:55:00', !             'modified' = '2013-12-20 16:00:00', !         ); !         $product = new Product($data); !         $product-setPdo($pdo); !         $product-save(); ! !         // The model has mentioning of productId !         $data['productId'] = null; !         $this-assertEquals($data, $product-toArray()); !     } 57
  • 77. Why  the  extra  work? • Your  databases  are  fully  tested,  no  need  to  do   it  yourself  again   • The  connec6ons  are  expensive  and  delay  your   tests   • Your  tes6ng  code  that  needs  to  handle  the  data   it  gets,  no  maQer  where  it  gets  it 59
  • 80. Joindin  Case:  talks.feryn.eu Fork  it:  hQps://github.com/ThijsFeryn/talks.feryn.eu 62
  • 81. API  Docs  are  your  friend 63
  • 82. Joindin  Test ?php ! class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase ! { !     protected $_joindin; !     protected $_settings; !      !     protected function setUp() !     { !         $this-_joindin = new Zftest_Service_Joindin(); !         $settings = simplexml_load_file(realpath( !             APPLICATION_PATH . '/../tests/_files/settings.xml')); !         $this-_settings = $settings-joindin; !         parent::setUp(); !     } !     protected function tearDown() !     { !         parent::tearDown(); !         $this-_joindin = null; !     } ! } 64
  • 83. Joindin  Test  (2) public function testJoindinCanGetUserDetails() ! { !     $expected = '?xml version=1.0?responseitemusernameDragonBe/ usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/ last_login/item/response'; !     $this-_joindin-setUsername($this-_settings-username) !                    -setPassword($this-_settings-password); !     $actual = $this-_joindin-user()-getDetail(); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } ! ! public function testJoindinCanCheckStatus() ! { !     $date = new DateTime(); !     $date-setTimezone(new DateTimeZone('UTC')); !     $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/ dttest_stringtesting unit test/test_string/response'; !     $actual = $this-_joindin-site()-getStatus('testing unit test'); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } 65
  • 86. Euh…  what  just  happened? 1) Zftest_Service_JoindinTest::testJoindinCanGetUserDetails Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ ID19/ID last_login1303248639/last_login + last_login1303250271/last_login /item /response 67
  • 87. And  this? 2) Zftest_Service_JoindinTest::testJoindinCanCheckStatus Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ ?xml version=1.0? response - dtTue, 19 Apr 2011 22:26:40 +0000/dt + dtTue, 19 Apr 2011 22:26:41 +0000/dt test_stringtesting unit test/test_string /response 68
  • 88. No  dispair,  we  help  you  out! 69
  • 89. Let’s  mock  the  HTTP  client ?php ! class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase ! { !     protected $_joindin; !     protected $_settings; !      !     protected function setUp() !     { !         $this-_joindin = new Zftest_Service_Joindin(); !         $client = new Zend_Http_Client(); !         $client-setAdapter(new Zend_Http_Client_Adapter_Test()); !         $this-_joindin-setClient($client); !         $settings = simplexml_load_file(realpath( !             APPLICATION_PATH . '/../tests/_files/settings.xml')); !         $this-_settings = $settings-joindin; !         parent::setUp(); !     } !     protected function tearDown() !     { !         parent::tearDown(); !         $this-_joindin = null; !     } ! } 70
  • 90. Let’s  mock  the  HTTP  client ?php ! class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase ! { !     protected $_joindin; !     protected $_settings; !      !     protected function setUp() !     { !         $this-_joindin = new Zftest_Service_Joindin(); !         $client = new Zend_Http_Client(); ! $client-setAdapter(new Zend_Http_Client_Adapter_Test());         $client-setAdapter(new Zend_Http_Client_Adapter_Test()); ! $this-_joindin-setClient($client); !         $this-_joindin-setClient($client); !         $settings = simplexml_load_file(realpath( ! $settings = simplexml_load_file(realpath( !             APPLICATION_PATH . '/../tests/_files/settings.xml')); ! APPLICATION_PATH . '/../tests/_files/settings.xml'));         $this-_settings = $settings-joindin; !         parent::setUp(); !     } !     protected function tearDown() !     { !         parent::tearDown(); !         $this-_joindin = null; !     } ! } ! 70
  • 91. Mocking  the  response public function testJoindinCanGetUserDetails() ! { !     $response = EOS ! HTTP/1.1 200 OK ! Content-type: text/xml ! ! ?xml version=1.0? ! response !   item !     usernameDragonBe/username !     full_nameMichelangelo van Dam/full_name !     ID19/ID !     last_login1303248639/last_login !   /item ! /response         ! EOS; !     $client = $this-_joindin-getClient()-getAdapter()-setResponse($response); !     $expected = '?xml version=1.0?responseitemusernameDragonBe/ usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/ last_login/item/response'; !     $this-_joindin-setUsername($this-_settings-username) !                    -setPassword($this-_settings-password); !     $actual = $this-_joindin-user()-getDetail(); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } 71
  • 92. Mocking  the  response public function testJoindinCanGetUserDetails() ! { !     $response = EOS ! HTTP/1.1 200 OK ! Content-type: text/xml ! ! ?xml version=1.0? ! response !   item !     usernameDragonBe/username !     full_nameMichelangelo van Dam/full_name !     ID19/ID !     last_login1303248639/last_login !   /item ! /response         ! EOS; ! $client = $this-_joindin-getClient()-getAdapter()-setResponse($response);     $client = $this-_joindin-getClient()-getAdapter()-setResponse($response); !     $expected = '?xml version=1.0?responseitemusernameDragonBe/ usernamefull_nameMichelangelo van Dam/full_nameID19/IDlast_login1303248639/ last_login/item/response'; !     $this-_joindin-setUsername($this-_settings-username) !                    -setPassword($this-_settings-password); !     $actual = $this-_joindin-user()-getDetail(); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } 71
  • 93. Same  here public function testJoindinCanCheckStatus() ! { !     $date = new DateTime(); !     $date-setTimezone(new DateTimeZone('UTC')); !     $response = EOS ! HTTP/1.1 200 OK ! Content-type: text/xml ! ! ?xml version=1.0? ! response !   dt{$date-format('r')}/dt !   test_stringtesting unit test/test_string ! /response         ! EOS; ! !     $client = $this-_joindin-getClient() !                              -getAdapter()-setResponse($response);! !     $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/ dttest_stringtesting unit test/test_string/response'; !     $actual = $this-_joindin-site()-getStatus('testing unit test'); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } 72
  • 94. Same  here public function testJoindinCanCheckStatus() ! { !     $date = new DateTime(); !     $date-setTimezone(new DateTimeZone('UTC')); !     $response = EOS ! HTTP/1.1 200 OK ! Content-type: text/xml ! ! ?xml version=1.0? ! response !   dt{$date-format('r')}/dt !   test_stringtesting unit test/test_string ! /response         ! EOS; ! ! $client = $this-_joindin-getClient() !     $client = $this-_joindin-getClient() !                          -getAdapter()-setResponse($response);                              -getAdapter()-setResponse($response);! !     $expected = '?xml version=1.0?responsedt' . $date-format('r') . '/ dttest_stringtesting unit test/test_string/response'; !     $actual = $this-_joindin-site()-getStatus('testing unit test'); !     $this-assertXmlStringEqualsXmlString($expected, $actual); ! } 72
  • 99. Even  for  spaghen  code 76
  • 100. Posi6ve    Nega6ve  tests 77
  • 101. Doing  it  more,  makes  you  beQer 78
  • 102. Recommended  reading Click on the images to view www.owasp.org planet.phpunit.de 79
  • 103. #PHPBNL14 January 25 - 26, 2014 phpcon.eu 80
  • 104. https://joind.in/10113 If you liked it, thank you! If not, tell me how to improve this talk 81
  • 105. Michelangelo van Dam Zend Certified Engineer ! michelangelo@in2it.be PHP Consulting - QA audits - Training ! www.in2it.be 82
  • 106. Credits • Me:  hQp://www.flickr.com/photos/akrabat/8784318813   • CrashTest:  hQp://www.flickr.com/photos/digi6zedchaos/3964206549   • Chris:  hQp://www.flickr.com/photos/akrabat/8421560178   • Nike:  hQp://www.flickr.com/photos/japokskee/4393860599   • Grads:  hQp://www.flickr.com/photos/ajschwegler/525829339   • Econopoly:  hQp://www.flickr.com/photos/danielbroche/2258988806   • Disaster:  hQp://www.flickr.com/photos/eschipul/1484495808/   • Mountain:  hQp://www.flickr.com/photos/jfdervin/2510535266   • Data  Store:  hQp://www.flickr.com/photos/comedynose/7048321621   • Protect:  hQp://www.flickr.com/photos/boltowlue/5724934828   • Owl:  hQp://www.flickr.com/photos/15016964@N02/9425608812   • Register:  hQp://www.flickr.com/photos/taedc/5466788868   • Crying  Baby:  hQp://www.flickr.com/photos/bibbit/5456802728   • Smiling  Donkey:  hQp://www.flickr.com/photos/smkybear/2239030703   • Jump  high:  hQp://www.flickr.com/photos/96748294@N06/9356699040   • Chipmunk:  hQp://www.flickr.com/photos/exfordy/1184487050   • Easy:  hQp://www.flickr.com/photos/dalismustaches/223972376   • Spaghen:  hQp://www.flickr.com/photos/lablasco/5512066970   • BaQery:  hQp://www.flickr.com/photos/shalf/6088539194   • Elephpant:  hQp://www.flickr.com/photos/dragonbe/11403208686 83