phpunit
How not to get roundkicked by Chuck Norris
Unit testing




   • wat is unit testen?
   • phpunit?
   • code coverage?
testcase (1)

Foo.php
 <?php
 class foo
 {
   public function baz() {
     return 1;
   }
 }




FooTest.php:
 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {
   public function testBlahBlah() {
     $foo = new Foo();
     $this->assertEquals($foo->baz(), 1);
   }
 }
testcase (2)

Foo.php
 <?php
 class foo
 {
   public function baz() {
     return 1;
   }
 }




FooTest.php:
 include_once "foo.php";

 class fooTest extends PHPUnit_Framework_TestCase
 {
         public function testBlahBlah() {
                 $foo = new Foo();
                 $this->assertEquals($foo->baz(), 1);
         }
 }
testcase (3)
testcase (4)

Foo.php
                                               FooTest.php:
   <?php
   class foo
   {                                            <?php
           public function baz() {
                   return 1;                    include_once "foo.php";
           }
                                                class fooTest extends PHPUnit_Framework_TestCase
           public function isOdd($number) {     {
                   if (($number & 1) == 1) {            public function testBlahBlah() {
                            return true;                        $foo = new Foo();
                   } else {                                     $this->assertEquals($foo->baz(), 1);
                            return false;               }
                   }
           }                                            public function testIsOdd() {
   }                                                            $foo = new Foo();
                                                                $this->assertTrue($foo->isOdd(1));
                                                        }
                                                }
testcase (5)
testcase (6)
testcase (7)


FooTest.php:
 <?php

 include_once "foo.php";

 class fooTest extends PHPUnit_Framework_TestCase
 {
         public function testBlahBlah() {
                 $foo = new Foo();
                 $this->assertEquals($foo->baz(), 1);
         }

          public function testIsOdd() {
                  $foo = new Foo();
                  $this->assertTrue($foo->isOdd(1));
         }

         public function testDoesIsOddReturnFalseWhenAnEvenDigitIsGiven {
                  $foo = new Foo();
                  $this->assertFalse($foo->isOdd(2));
          }
 }
testcase (8)
PHPUnit chapters




  • setUp(), tearDown()
  • @depends
  • @dataproviders
  • @exception testing
  • mocking & stubbing
setUp(), tearDown() (1)




 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {
   public function testBlahBlah() {
     $foo = new Bar();
     ....
   }
   public function testBlah() {
     $foo = new Bar();
     ....
   }

 }
setUp(), tearDown() (2)


 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {
   public setUp() {
     $this->_foo = new Bar();
   }

     public tearDown() {
       unset($this->_foo);
     }

     public function testBlahBlah() {
       ....
     }
     public function testBlah() {
       ....
     }

 }
@depends (1)



 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {

     public function testFoo() {
       ....
     }

     /**
       * @depends testFoo
       */
     public function testBar() {
        ....
     }

 }
@dataprovider (1)




 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {

     public function testCount() {
       $foo = new Foo();
       $this->assertEquals($foo->add(1, 2), 3);
       $this->assertEquals($foo->add(10,-4), 6);
       $this->assertEquals($foo->add(4, 6), 10);
       ...
     }

 }
@dataprovider (2)


 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {

     /**
       * @dataprovider countProvider
       */
     public function testCount($a, $b, $c) {
        $foo = new Foo();
        $this->assertEquals($foo->add($a, $b), $c);
     }

     public function countProvider() {
       return array(
          array(1, 2, 3),
          array(10, -4, 6),
          array(4, 6, 10)
       );
     }

 }
@expectedException (1)



 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {

     public function testCount() {
       $foo = new Foo();
       try {
         $foo->functionWithException();
         $this->fail(“An exception should have occurred”);
       } catch (Exception $e) {

         }
     }

 }
@expectedException (2)




 <?php
 class fooTest extends PHPUnit_Framework_TestCase
 {

     /**
       * @expectedException Exception
       */
     public function testCount() {
        $foo = new Foo();
        $foo->functionWithException();
     }

 }
Mocking & Stubbing (1)


 <?php

  class MyMailer {
        protected $_from;
        protected $_body;
        protected $_subject;

         function setFrom($from) {
                 $this->_from = $from;
         }

         .......

         function mail(array $to) {
                 $count = 0;
                 foreach ($to as $recipient) {

                 }
                         if ($this->_mail($recipient)) $count++;
                                                                    Isolated
                 return $count;
         }

         protected function _mail($to) {
                 return mail($to, $this->_subject, $this->_body);
         }
  }
Mocking & Stubbing (2)


 <?php

     include_once "mail.php";

 class Mock_MyMailer extends MyMailer {
         protected function _mail($to) {
                 return true;
         }
 }

 class mailTest extends PHPUnit_Framework_TestCase
 {
         ...

           public function testMail() {
                   $mailer = new Mock_MyMailer();
                   $mailer->setSubject("blaat");
                   $mailer->setFrom("jthijssen@example.com");
                   $mailer->setBody("meeh");

                   $to = array("aap@example.com", "blaat@example.com", "all@example.com");
                   $this->assertEquals($mailer->mail($to), 3);
           }
 }
Mocking & Stubbing (3)



 <?php

 include_once "mail.php";

 class mailTest extends PHPUnit_Framework_TestCase
 {
         ...

         public function testMail() {

                 $mailer = new MyMailer();
                 $mailer->setSubject("blaat");
                 $mailer->setFrom("jthijssen@example.com");
                 $mailer->setBody("meeh");

                 $to = array("aap@example.com", "blaat@example.com", "all@example.com");
                 $this->assertEquals($mailer->mail($to), 3);
         }
 }
Mocking & Stubbing (4)




 <?php

 class mailTest extends PHPUnit_Framework_TestCase
 {
         public function testMail() {
                 $stub = $this->getMockBuilder('MyMailer', array('_mail'))->getMock();
                 $stub->expects($this->any())
                      ->method('_mail')
                      ->will($this->returnValue(true));

                 $stub->setSubject("blaat");
                 $stub->setFrom("jthijssen@example.com");
                 $stub->setBody("meeh");

                 $to = array("aap@example.com", "blaat@example.com", "all@example.com");
                 $this->assertEquals($stub->mail($to), 3);
         }
 }
Difficult cases (1)




   public function getItem($id) {
     $client = new Zend_Http_Client();
     $client->setUri(‘http://my.api’);
     $client->setParameterPost(‘ID’,$id);
     $client->setMethod(Zend-Http_Client::POST);
     $request = $client->request();

       $item = $request->getBody();
       $item = strtolower($item);
       return $item;
   }




∂ TESTABLE?
Difficult cases (2)




   public function __construct() {
       $this->_adapter = new Zend_Http_Client_Adapter_Socket();
   }

   public function getItem($id) {
     $client = new Zend_Http_Client();
     $client->setConfig(array(‘adapter’ => $this->getAdapter()));
     $client->setUri(‘http://my.api’);
     $client->setParameterPost(‘ID’,$id);
     $client->setMethod(Zend-Http_Client::POST);
     $request = $client->request();

       $item = $request->getBody();
       $item = strtolower($item);
       return $item;
   }




∂ “DECOUPLE” ADAPTER
Difficult cases (3)




   function testGetItem() {
     $adapter = new Zend_Http_Client_Adapter_Test();
     $adapter->setResponse(“HTTP/1.1 200 OKrnrnBLAAT”);
     $adapter->addResponse(“HTTP/1.1 200 OKrnrnAAP”);

       $foo = new Foo();
       $foo->setAdapter($adapter);
       $this->assertEquals($foo->getItem(1), ‘blaat’);
       $this->assertEquals($foo->getItem(2), ‘aap’);
   }




∂ IT’S TESTABLE AGAIN
Difficult cases (4)




    Gebruik “dependency injection” ipv
    “tight coupling”.
Dependency Injection (1)




   public function pay(Article $article) {
     $psp = new PaymentProvider();
     $psp->setAmount($article->getAmount());
     return $psp->pay();
   }




∂ THIGHT COUPLING AGAIN
Dependency Injection (2)




  public function setPaymentProvider(PaymentProvider $psp) {
    $this->_psp = $psp;
  }

  public function getPaymentProvider() {
    return $this->_psp;
  }

  public function pay(Article $article) {
    $psp = $this->getPaymentProvider();
    $psp->setAmount($article->getAmount());
    return $psp->pay();
  }




∂ DECOUPLE PAYMENT PROVIDER
Dependency Injection (3)




 public function testPayAccepted() {
   $stub = $this->getMockBuilder(‘paymentprovider’, array(‘pay’))->getMock();

     $stub->expects($this->any())
          ->method(‘pay’)
          ->will($this->returnValue(true));

     $article = new Article();
     $article->setAmount(15);

     $foo = new Foo();
     $foo->setPaymentProvider($stub);
     $this->assertTrue($foo->pay($article));

     $this->assertEquals($user->getAmount(), 85);
 }




∂ TESTABLE *AND* EXTENDABLE
Dependency Injection (4)




  public function testPayFailed() {
    $stub = $this->getMockBuilder(‘paymentprovider’, array(‘pay’))->getMock();

    $stub->expects($this->any())
         ->method(‘pay’)
         ->will($this->returnValue(false));

    $article = new Article();
    $article->setAmount(15);

    $foo = new Foo();
    $foo->setPaymentProvider($stub);
    $this->assertFalse($foo->pay($article));

  $this->assertEquals($user->getAmount(), 100);
  }




∂ TESTABLE *AND* EXTENDABLE
Workshop



   • Login op php5dev02
   • directory: workshop
   • http://workshop.<username>.dev/
      tests/coverage

   • cd tests
   • ./runtest.sh
∂ FOLLOW INSTRUCTIONS
∂ THANK YOU FOR YOUR ATTENTION

Workshop unittesting

  • 1.
    phpunit How not toget roundkicked by Chuck Norris
  • 2.
    Unit testing • wat is unit testen? • phpunit? • code coverage?
  • 3.
    testcase (1) Foo.php <?php class foo { public function baz() { return 1; } } FooTest.php: <?php class fooTest extends PHPUnit_Framework_TestCase { public function testBlahBlah() { $foo = new Foo(); $this->assertEquals($foo->baz(), 1); } }
  • 4.
    testcase (2) Foo.php <?php class foo { public function baz() { return 1; } } FooTest.php: include_once "foo.php"; class fooTest extends PHPUnit_Framework_TestCase { public function testBlahBlah() { $foo = new Foo(); $this->assertEquals($foo->baz(), 1); } }
  • 5.
  • 6.
    testcase (4) Foo.php FooTest.php: <?php class foo { <?php public function baz() { return 1; include_once "foo.php"; } class fooTest extends PHPUnit_Framework_TestCase public function isOdd($number) { { if (($number & 1) == 1) { public function testBlahBlah() { return true; $foo = new Foo(); } else { $this->assertEquals($foo->baz(), 1); return false; } } } public function testIsOdd() { } $foo = new Foo(); $this->assertTrue($foo->isOdd(1)); } }
  • 7.
  • 8.
  • 9.
    testcase (7) FooTest.php: <?php include_once "foo.php"; class fooTest extends PHPUnit_Framework_TestCase { public function testBlahBlah() { $foo = new Foo(); $this->assertEquals($foo->baz(), 1); } public function testIsOdd() { $foo = new Foo(); $this->assertTrue($foo->isOdd(1)); } public function testDoesIsOddReturnFalseWhenAnEvenDigitIsGiven { $foo = new Foo(); $this->assertFalse($foo->isOdd(2)); } }
  • 10.
  • 11.
    PHPUnit chapters • setUp(), tearDown() • @depends • @dataproviders • @exception testing • mocking & stubbing
  • 12.
    setUp(), tearDown() (1) <?php class fooTest extends PHPUnit_Framework_TestCase { public function testBlahBlah() { $foo = new Bar(); .... } public function testBlah() { $foo = new Bar(); .... } }
  • 13.
    setUp(), tearDown() (2) <?php class fooTest extends PHPUnit_Framework_TestCase { public setUp() { $this->_foo = new Bar(); } public tearDown() { unset($this->_foo); } public function testBlahBlah() { .... } public function testBlah() { .... } }
  • 14.
    @depends (1) <?php class fooTest extends PHPUnit_Framework_TestCase { public function testFoo() { .... } /** * @depends testFoo */ public function testBar() { .... } }
  • 15.
    @dataprovider (1) <?php class fooTest extends PHPUnit_Framework_TestCase { public function testCount() { $foo = new Foo(); $this->assertEquals($foo->add(1, 2), 3); $this->assertEquals($foo->add(10,-4), 6); $this->assertEquals($foo->add(4, 6), 10); ... } }
  • 16.
    @dataprovider (2) <?php class fooTest extends PHPUnit_Framework_TestCase { /** * @dataprovider countProvider */ public function testCount($a, $b, $c) { $foo = new Foo(); $this->assertEquals($foo->add($a, $b), $c); } public function countProvider() { return array( array(1, 2, 3), array(10, -4, 6), array(4, 6, 10) ); } }
  • 17.
    @expectedException (1) <?php class fooTest extends PHPUnit_Framework_TestCase { public function testCount() { $foo = new Foo(); try { $foo->functionWithException(); $this->fail(“An exception should have occurred”); } catch (Exception $e) { } } }
  • 18.
    @expectedException (2) <?php class fooTest extends PHPUnit_Framework_TestCase { /** * @expectedException Exception */ public function testCount() { $foo = new Foo(); $foo->functionWithException(); } }
  • 19.
    Mocking & Stubbing(1) <?php class MyMailer { protected $_from; protected $_body; protected $_subject; function setFrom($from) { $this->_from = $from; } ....... function mail(array $to) { $count = 0; foreach ($to as $recipient) { } if ($this->_mail($recipient)) $count++; Isolated return $count; } protected function _mail($to) { return mail($to, $this->_subject, $this->_body); } }
  • 20.
    Mocking & Stubbing(2) <?php include_once "mail.php"; class Mock_MyMailer extends MyMailer { protected function _mail($to) { return true; } } class mailTest extends PHPUnit_Framework_TestCase { ... public function testMail() { $mailer = new Mock_MyMailer(); $mailer->setSubject("blaat"); $mailer->setFrom("jthijssen@example.com"); $mailer->setBody("meeh"); $to = array("aap@example.com", "blaat@example.com", "all@example.com"); $this->assertEquals($mailer->mail($to), 3); } }
  • 21.
    Mocking & Stubbing(3) <?php include_once "mail.php"; class mailTest extends PHPUnit_Framework_TestCase { ... public function testMail() { $mailer = new MyMailer(); $mailer->setSubject("blaat"); $mailer->setFrom("jthijssen@example.com"); $mailer->setBody("meeh"); $to = array("aap@example.com", "blaat@example.com", "all@example.com"); $this->assertEquals($mailer->mail($to), 3); } }
  • 22.
    Mocking & Stubbing(4) <?php class mailTest extends PHPUnit_Framework_TestCase { public function testMail() { $stub = $this->getMockBuilder('MyMailer', array('_mail'))->getMock(); $stub->expects($this->any()) ->method('_mail') ->will($this->returnValue(true)); $stub->setSubject("blaat"); $stub->setFrom("jthijssen@example.com"); $stub->setBody("meeh"); $to = array("aap@example.com", "blaat@example.com", "all@example.com"); $this->assertEquals($stub->mail($to), 3); } }
  • 23.
    Difficult cases (1) public function getItem($id) { $client = new Zend_Http_Client(); $client->setUri(‘http://my.api’); $client->setParameterPost(‘ID’,$id); $client->setMethod(Zend-Http_Client::POST); $request = $client->request(); $item = $request->getBody(); $item = strtolower($item); return $item; } ∂ TESTABLE?
  • 24.
    Difficult cases (2) public function __construct() { $this->_adapter = new Zend_Http_Client_Adapter_Socket(); } public function getItem($id) { $client = new Zend_Http_Client(); $client->setConfig(array(‘adapter’ => $this->getAdapter())); $client->setUri(‘http://my.api’); $client->setParameterPost(‘ID’,$id); $client->setMethod(Zend-Http_Client::POST); $request = $client->request(); $item = $request->getBody(); $item = strtolower($item); return $item; } ∂ “DECOUPLE” ADAPTER
  • 25.
    Difficult cases (3) function testGetItem() { $adapter = new Zend_Http_Client_Adapter_Test(); $adapter->setResponse(“HTTP/1.1 200 OKrnrnBLAAT”); $adapter->addResponse(“HTTP/1.1 200 OKrnrnAAP”); $foo = new Foo(); $foo->setAdapter($adapter); $this->assertEquals($foo->getItem(1), ‘blaat’); $this->assertEquals($foo->getItem(2), ‘aap’); } ∂ IT’S TESTABLE AGAIN
  • 26.
    Difficult cases (4) Gebruik “dependency injection” ipv “tight coupling”.
  • 27.
    Dependency Injection (1) public function pay(Article $article) { $psp = new PaymentProvider(); $psp->setAmount($article->getAmount()); return $psp->pay(); } ∂ THIGHT COUPLING AGAIN
  • 28.
    Dependency Injection (2) public function setPaymentProvider(PaymentProvider $psp) { $this->_psp = $psp; } public function getPaymentProvider() { return $this->_psp; } public function pay(Article $article) { $psp = $this->getPaymentProvider(); $psp->setAmount($article->getAmount()); return $psp->pay(); } ∂ DECOUPLE PAYMENT PROVIDER
  • 29.
    Dependency Injection (3) public function testPayAccepted() { $stub = $this->getMockBuilder(‘paymentprovider’, array(‘pay’))->getMock(); $stub->expects($this->any()) ->method(‘pay’) ->will($this->returnValue(true)); $article = new Article(); $article->setAmount(15); $foo = new Foo(); $foo->setPaymentProvider($stub); $this->assertTrue($foo->pay($article)); $this->assertEquals($user->getAmount(), 85); } ∂ TESTABLE *AND* EXTENDABLE
  • 30.
    Dependency Injection (4) public function testPayFailed() { $stub = $this->getMockBuilder(‘paymentprovider’, array(‘pay’))->getMock(); $stub->expects($this->any()) ->method(‘pay’) ->will($this->returnValue(false)); $article = new Article(); $article->setAmount(15); $foo = new Foo(); $foo->setPaymentProvider($stub); $this->assertFalse($foo->pay($article)); $this->assertEquals($user->getAmount(), 100); } ∂ TESTABLE *AND* EXTENDABLE
  • 31.
    Workshop • Login op php5dev02 • directory: workshop • http://workshop.<username>.dev/ tests/coverage • cd tests • ./runtest.sh ∂ FOLLOW INSTRUCTIONS
  • 32.
    ∂ THANK YOUFOR YOUR ATTENTION