Introduction to PHPUnit & Best Practices

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    5 Favorites & 1 Group

    Introduction to PHPUnit & Best Practices - Presentation Transcript

    1. Introduction to PHPUnit & Best Practices Sebastian Bergmann July 20th  2009
    2. Who I am  Sebastian Bergmann  Involved in the PHP project since 2000  Creator of PHPUnit  Co-Founder and Principal Consultant with thePHP.cc
    3. The Bank Account Example BankAccount.php <?php class BankAccount { protected $balance = 0; public function getBalance() { return $this->balance; } protected function setBalance($balance) { if ($balance >= 0) { $this->balance = $balance; } else { throw new RuntimeException; } } public function depositMoney($balance) { $this->setBalance($this->getBalance() + $balance); } public function withdrawMoney($balance) { $this->setBalance($this->getBalance() - $balance); } } ?>
    4. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { }
    5. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { public function testBalanceIsInitiallyZero() { } }
    6. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { public function testBalanceIsInitiallyZero() { $ba = new BankAccount; $this->assertEquals(0, $ba->getBalance()); return $ba; } }
    7. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { public function testBalanceIsInitiallyZero() { $ba = new BankAccount; $this->assertEquals(0, $ba->getBalance()); return $ba; } /** * @depends testBalanceIsInitiallyZero * @expectedException RuntimeException */ public function testBalanceCannotBecomeNegative(BankAccount $ba) { $ba->withdrawMoney(1); } }
    8. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { public function testBalanceIsInitiallyZero() { $ba = new BankAccount; $this->assertEquals(0, $ba->getBalance()); return $ba; } /** * @depends testBalanceIsInitiallyZero * @expectedException RuntimeException */ public function testBalanceCannotBecomeNegative(BankAccount $ba) { $ba->withdrawMoney(1); } /** * @depends testBalanceIsInitiallyZero * @expectedException RuntimeException */ public function testBalanceCannotBecomeNegative2(BankAccount $ba) { $ba->depositMoney(-1); } // ... }
    9. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { // ... /** * @depends testBalanceIsInitiallyZero */ public function testDepositingMoneyWorks(BankAccount $ba) { $ba->depositMoney(1); $this->assertEquals(1, $ba->getBalance()); return $ba; } }
    10. The Bank Account Example BankAccountTest.php <?php require_once 'BankAccount.php'; class BankAccountTest extends PHPUnit_Framework_TestCase { // ... /** * @depends testBalanceIsInitiallyZero */ public function testDepositingMoneyWorks(BankAccount $ba) { $ba->depositMoney(1); $this->assertEquals(1, $ba->getBalance()); return $ba; } /** * @depends testDepositingMoneyWorks */ public function testWithdrawingMoneyWorks(BankAccount $ba) { $ba->withdrawMoney(1); $this->assertEquals(0, $ba->getBalance()); } }
    11. The Bank Account Example Running tests sb@ubuntu ~ % phpunit BankAccountTest PHPUnit 3.4.0 by Sebastian Bergmann. ..... Time: 0 seconds OK (5 tests, 5 assertions)
    12. The Bank Account Example BankAccount.php <?php class BankAccount { protected $balance = 1; public function getBalance() { return $this->balance; } protected function setBalance($balance) { if ($balance >= 0) { $this->balance = $balance; } else { throw new RuntimeException; } } public function depositMoney($balance) { $this->setBalance($this->getBalance() + $balance); } public function withdrawMoney($balance) { $this->setBalance($this->getBalance() - $balance); } } ?>
    13. The Bank Account Example Running tests sb@ubuntu ~ % phpunit BankAccountTest PHPUnit 3.4.0 by Sebastian Bergmann. FSSSS Time: 0 seconds There was 1 failure: 1) BankAccountTest::testBalanceIsInitiallyZero Failed asserting that <integer:1> matches expected <integer:0>. /home/sb/BankAccountTest.php:7 FAILURES! Tests: 1, Assertions: 1, Failures: 1, Skipped: 4.
    14. The Bank Account Example Running tests sb@ubuntu ~ % phpunit --verbose BankAccountTest PHPUnit 3.4.0 by Sebastian Bergmann. FSSSS Time: 0 seconds There was 1 failure: 1) BankAccountTest::testBalanceIsInitiallyZero Failed asserting that <integer:1> matches expected <integer:0>. /home/sb/BankAccountTest.php:7 There were 4 skipped tests: 1) BankAccountTest::testBalanceCannotBecomeNegative This test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass. 2) BankAccountTest::testBalanceCannotBecomeNegative2 This test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass. 3) BankAccountTest::testDepositingMoneyWorks This test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass. 4) BankAccountTest::testWithdrawingMoneyWorks This test depends on "BankAccountTest::testDepositingMoneyWorks" to pass. FAILURES! Tests: 1, Assertions: 1, Failures: 1, Skipped: 4.
    15. The Bank Account Example TestDox: Report test result as executable specification sb@ubuntu ~ % phpunit --testdox BankAccountTest PHPUnit 3.4.0 by Sebastian Bergmann. BankAccount [x] Balance is initially zero [x] Balance cannot become negative [x] Depositing money works [x] Withdrawing money works
    16. The Bank Account Example Code Coverage sb@ubuntu ~ % phpunit --coverage-html /tmp/report BankAccountTest PHPUnit 3.4.0 by Sebastian Bergmann. ..... Time: 0 seconds OK (5 tests, 5 assertions) Generating code coverage report, this may take a moment.
    17. ”The secret in testing is in writing testable code” ­ Miško Hevery
    18. Untestable Code Most people say Real issues  Make things private  Mix new with logic  Use final keyword  Look for things  Write long methods  Work in constructor  ...  Singletons  Global state  Static methods This slide contains material by Miško Hevery
    19. Untestable Code Object Graph Construction Issues <?php Mixing object graph class Document { construction with work private $html; public function __construct($url) { $client = new HttpClient; $this->html = $client->get($url); } } Doing work in the constructor This slide contains material by Miško Hevery
    20. Untestable Code Object Graph Construction Issues <?php class Document { private $html; public function __construct(HttpClient $client, $url) { $this->html = $client->get($url); } } Doing work in the constructor This slide contains material by Miško Hevery
    21. Untestable Code Decouple object graph construction from other work <?php class DocumentFactory { public function __construct(HttpClient $client) { $this->client = $client; } public function build($url) { return new Document($this->client->get($url)); } } This slide contains material by Miško Hevery
    22. Untestable Code Decouple object graph construction from other work <?php class DocumentFactory { public function __construct(HttpClient $client) { $this->client = $client; } public function build($url) { return new Document($this->client->get($url)); } } class Document { private $html; public function __construct($html) { $this->html = $html; } } This slide contains material by Miško Hevery
    23. Untestable Code Global State  Global variables  Static variables in classes  Static variables in functions and methods  Persistent Data  Database  Filesystem  Distributed Data  memcached
    24. Untestable Code Global Variables in PHP  A variable $foo declared in the global scope is stored in $GLOBALS['foo'].  $GLOBALS is a so-called superglobal.  Superglobals are built-in variables that are available in all scopes.  $GLOBALS['foo'] can be used to access the global variable $foo in the scope of a function or method  global $foo; can be used to create a local variable with a reference to the global variable.
    25. Untestable Code Global Variables in PHP $GLOBALS = array( 'GLOBALS' => &$GLOBALS, '_ENV' => array(), '_POST' => array(), '_GET' => array(), '_COOKIE' => array(), '_SERVER' => array(), '_FILES' => array(), '_REQUEST' => array() );
    26. Untestable Code PHPUnit and Global Variables  Default Behaviour  Backup $GLOBALS before the execution of a test  Run the test  Restore $GLOBALS after the execution of a test  $backupGlobals = FALSE  Do not backup and restore $GLOBALS  $backupGlobalsBlacklist = array()  Backup and restore $GLOBALS but not the variables listed in the array
    27. Untestable Code PHPUnit and Static Attributes  Default Behaviour  Backup static attributes of user-defined classes before the execution of a test  Run the test  Restore static attributes of user-defined classes after the execution of a test  $backupStaticAttributes = FALSE  Do not backup and restore static attributes of user- defined classes  $backupStaticAttributesBlacklist = array()  Backup and restore static attributes of user-defined classes but not the variables listed in the array
    28. Untestable Code Hidden Global State  Loaded source files  Loaded classes and functions  Sent HTTP headers  new DateTime()  time()  mktime()  rand() This slide contains material by Miško Hevery
    29. Testable Code Selected Aspects of Testability  Few Branches  More branches increase complexity  Higher complexity leads to more errors  Higher complexity makes testing harder  Few Dependencies  Improved isolation for defect localization  Should be injectable to be replacable by test-specific equivalents
    30. PHPUnit Best Practices Use an XML Configuration File <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" syntaxCheck="false"> </phpunit>
    31. PHPUnit Best Practices Use an XML Configuration File <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" syntaxCheck="false"> <testsuites> <testsuite name="My Test Suite"> <directory>path/to/dir</directory> </testsuite> </testsuites> </phpunit>
    32. PHPUnit Best Practices Use an XML Configuration File <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" syntaxCheck="false"> <php> <const name="foo" value="bar"/> <var name="foo" value="bar"/> <ini name="foo" value="bar"/> </php> </phpunit>
    33. PHPUnit Best Practices Use Code Coverage Whitelisting <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" syntaxCheck="false"> <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">path/to/dir</directory> </whitelist> </filter> </phpunit>
    34. PHPUnit Best Practices Make the Code Coverage information more meaningful /** * @covers Object_Freezer::freeze */ public function testFreezingAnObjectWorks() { $this->assertEquals( array( /* ... */ ), $this->freezer->freeze(new A(1, 2, 3)) ); }
    35. PHPUnit Best Practices Exploit dependencies between tests /** * @covers Object_Freezer::freeze * @covers Object_Freezer::thaw * @depends testFreezingAnObjectWorks */ public function testFreezingAndThawingAnObjectWorks() { $object = new A(1, 2, 3); $this->assertEquals( $object, $this->freezer->thaw( $this->freezer->freeze($object) ) ); }
    36. PHPUnit Best Practices Decouple test code from complex / large sets of test data <?php class DataTest extends PHPUnit_Framework_TestCase { /** * @dataProvider providerMethod */ public function testAdd($a, $b, $c) { $this->assertEquals($c, $a + $b); } public function providerMethod() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 1, 3), array(1, 0, 1) ); } }
    37. PHPUnit Best Practices Decouple test code from complex / large sets of test data sb@ubuntu ~ % phpunit DataTest PHPUnit 3.4.0 by Sebastian Bergmann. ..F. Time: 0 seconds There was 1 failure: 1) testAdd(DataTest) with data (1, 1, 3) Failed asserting that <integer:2> matches expected value <integer:3>. /home/sb/DataTest.php:19 FAILURES! Tests: 4, Assertions: 4, Failures: 1.
    38. PHPUnit Best Practices Write code that allows you to disable PHPUnit features  Syntax Check  Backup/Restore of global variables  Backup/Restore of static attributes
    39. The End Thank you for your interest! These slides will be posted on http://slideshare.net/sebastian_bergmann
    40. License   This presentation material is published under the Attribution-Share Alike 3.0 Unported license.   You are free: ✔ to Share – to copy, distribute and transmit the work. ✔ to Remix – to adapt the work.   Under the following conditions: ● Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). ● Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.   For any reuse or distribution, you must make clear to others the license terms of this work.   Any of the above conditions can be waived if you get permission from the copyright holder.   Nothing in this license impairs or restricts the author's moral rights.

    + Sebastian BergmannSebastian Bergmann, 4 months ago

    custom

    2027 views, 5 favs, 1 embeds more stats

    Although Unit Testing is a recommended practice for more

    More info about this document

    CC Attribution-ShareAlike LicenseCC Attribution-ShareAlike License

    Go to text version

    • Total Views 2027
      • 2020 on SlideShare
      • 7 from embeds
    • Comments 0
    • Favorites 5
    • Downloads 44
    Most viewed embeds
    • 7 views on http://192.168.10.3

    more

    All embeds
    • 7 views on http://192.168.10.3

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories

    Tags

    Groups / Events