Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
South Florida PHP Meetup Group
   James Fuller
   Web Developer
   http://www.jblotus.com

   Started unit testing in response to a new job
    devel...
   Afraid to make even tiny code changes due to
    unpredictable side-effects?

   Been burned by bugs that came back t...
Imagine your code as a dark twisted forest that
you must navigate.

Nothing makes sense, there are no clear paths
and you ...
   Some will be brave and foolish, cutting
    corners to move quickly through the
    forest.

   Some will be paralyze...
Modify code


  Broke
something                  Run feature




God I hope                 Doesn't work
this works

     ...
   Write tests
   Use PHPUnit to automate your tests
   Practice Test-Driven-Development
   Improve your design
   En...
   Can you prove it works? automatically?

   Can you change it and know it still works?
    without loading the browser...
   All developers practice some form of testing.
    Most PHP developers do it in the browser.

   What we want to do is...
   Before you write new code
   When you fix a bug
   Before you begin refactoring code
   As you go, improving code c...
   The principle of unit testing is to verify that a
    unit of code will function correctly in
    isolation.

   A un...
Understand
            Requirements


Refactor                   Write tests



Run Tests                   Run Tests
 (PA...
   Focus on design of system before
    implementation
   Changes are easier to make, with less side
    effects
   Doc...
   PHPUnit is an automated testing framework
    PHP development based upon xUnit (java).

   PHPUnit gives us an easy w...
Command line test runner
Mature and expansive assertion library
Code coverage
Mock objects
Lots of extensions
Fast & Confi...
   PEAR
     best for system wide access to phpunit
     hard to downgrade or change versions
     dependencies can be...
   Upgrade PEAR to the latest version
     sudo pear upgrade PEAR



   Install PHPUnit
     pear config-set auto_disc...
   pear   install   phpunit/DbUnit
   pear   install   phpunit/PHPUnit_Selenium
   pear   install   phpunit/PHPUnit_Sto...
   Checkout source from github
    mkdir phpunit && cd phpunit
    git clone git://github.com/sebastianbergmann/phpunit.g...
   Usage
     phpunit [switches] UnitTest [UnitTest.php]
     phpunit [switches] <directory>

   Common Switches
    ...
   PHPUnit runtime options can be defined
    using command line switches and/or xml
    (phpunit.xml)

   Bootstrap you...
It's easy!! Include the autoloader in your
bootstrap or in your test cases:

<?php
require_once 'PHPUnit/Autoload.php';
   A test suite contains any number of test
    cases.

   A test case is a class that contains individual
    tests.

...
/
    lib/
     Foo.php

    tests/
      suite/
       lib/
       FooTest.php
<phpunit>
  <testsuites>
    <testsuite name="All Tests">
      <directory>tests/suite</directory>
    </testsuite>
    <t...
A Fixture represents the known state of the
world.

   Object instantiation
   Database Records
   Preconditions & Post...
class FooTest extends PHPUnit_Framework_TestCase {

    //runs before each test method
    protected function setUp() {}

...
class FooTest extends PHPUnit_Framework_TestCase {
  protected $Foo;

    protected function setUp() {
      $this->Foo = ...
//method being tested in class Foo
public function reverseConcat($a, $b) {
  return strrev($a . $b);
}

//test method in c...
   An assertion is how we declare an expectation.

   Well-tested systems rely upon verification, not
    developer intu...
assertArrayHasKey()               assertNull()
assertClassHasAttribute()         assertObjectHasAttribute()
assertClassHas...
When assertions evaluate to TRUE the test will emit a PASS


 $this->assertTrue(true);
 $this->assertFalse(false);
 $this-...
When assertions evaluate to FALSE the test will emit a FAILURE


$this->assertTrue(false);
$this->assertFalse(true);
$this...
   game has 2 players
   players pick either rock, paper, or scissors
   rock beats scissors
   paper beats rock
   s...
   Isolation means that a unit of code is not
    affected by external factors.

   In practice we can't always avoid ex...
   Test doubles are objects we use to substitute
    dependencies in our system under test.

   Stubs return prebaked va...
   PHPUnit provides a built in mocking
    framework.

   You can easily create test doubles that you
    can inject int...
public function testStub() {

    $stub = $this->getMock('Stub', array('foo'));

    $stub->expects($this->once())
       ...
   Two ways to generate mock objects
   getMock() with arguments
   Mock Builder API
$this->getMock(
   $class_name,
   $mocked_methods,
   $constructor_args,
   $mock_class_name,
   $call_original_construct...
$this->getMockBuilder($class_name)
     ->setMethods(array $methods)
     ->disableOriginalConstructor()
     ->disableOri...
$mock = $this->getMockBuilder('Foo')
             ->setMethods(array('someMethod'))
             ->getMock();

$mock->expe...
$mock = $this->getMockBuilder('Foo')
             ->setMethods(array('someMethod'))
             ->getMock();

$mock->expe...
class WebService {
  protected $Http;

    public function __construct(Http $http) {
      $this->Http = $http;
    }

   ...
require_once 'PHPUnit/Autoload.php';
require_once 'WebService.php';

class TestWebService extends PHPUnit_Framework_TestCa...
   Static method calls

   Instantiating objects directly (hard
    coupling)

   External resources (network, file sys...
class Dependency {
}

class SystemUnderTest {

    public function badIdea() {
      $dependency = new Dependency;
    }
}
class TwitterClient {

    function getTweets() {
      $url = 'api.twitter.com/v2/tweets/...';
      return Http::consume...
class TwitterClient {

    protected $Http;

    public function __construct(Http $http) {
      $this->Http = $http;
    ...
class TestTwitterClient extends
PHPUnit_Framework_TestCase {

    public function testCantMockTwitter() {

        $mock =...
class Singleton {

    public $data;
    protected static $instance;

    private function __construct() {}
    private fu...
class SystemUnderTest {

    public function setData($data) {
      Singleton::getInstance()->data = $data;
    }

    pub...
class TestSystemUnderTest extends
PHPUnit_Framework_TestCase {

    public function testSingletonsAreFineAtFirst() {

    ...
   Many frameworks come with built in testing
    facilities, or the ability to easily integrate with
    PHPUnit.

   T...
   Get your test harness working
   Get your feet wet first, testing smaller/trivial
    code
   Manual verification is...
Fighting Fear-Driven-Development With PHPUnit
Upcoming SlideShare
Loading in …5
×

of

Fighting Fear-Driven-Development With PHPUnit Slide 1 Fighting Fear-Driven-Development With PHPUnit Slide 2 Fighting Fear-Driven-Development With PHPUnit Slide 3 Fighting Fear-Driven-Development With PHPUnit Slide 4 Fighting Fear-Driven-Development With PHPUnit Slide 5 Fighting Fear-Driven-Development With PHPUnit Slide 6 Fighting Fear-Driven-Development With PHPUnit Slide 7 Fighting Fear-Driven-Development With PHPUnit Slide 8 Fighting Fear-Driven-Development With PHPUnit Slide 9 Fighting Fear-Driven-Development With PHPUnit Slide 10 Fighting Fear-Driven-Development With PHPUnit Slide 11 Fighting Fear-Driven-Development With PHPUnit Slide 12 Fighting Fear-Driven-Development With PHPUnit Slide 13 Fighting Fear-Driven-Development With PHPUnit Slide 14 Fighting Fear-Driven-Development With PHPUnit Slide 15 Fighting Fear-Driven-Development With PHPUnit Slide 16 Fighting Fear-Driven-Development With PHPUnit Slide 17 Fighting Fear-Driven-Development With PHPUnit Slide 18 Fighting Fear-Driven-Development With PHPUnit Slide 19 Fighting Fear-Driven-Development With PHPUnit Slide 20 Fighting Fear-Driven-Development With PHPUnit Slide 21 Fighting Fear-Driven-Development With PHPUnit Slide 22 Fighting Fear-Driven-Development With PHPUnit Slide 23 Fighting Fear-Driven-Development With PHPUnit Slide 24 Fighting Fear-Driven-Development With PHPUnit Slide 25 Fighting Fear-Driven-Development With PHPUnit Slide 26 Fighting Fear-Driven-Development With PHPUnit Slide 27 Fighting Fear-Driven-Development With PHPUnit Slide 28 Fighting Fear-Driven-Development With PHPUnit Slide 29 Fighting Fear-Driven-Development With PHPUnit Slide 30 Fighting Fear-Driven-Development With PHPUnit Slide 31 Fighting Fear-Driven-Development With PHPUnit Slide 32 Fighting Fear-Driven-Development With PHPUnit Slide 33 Fighting Fear-Driven-Development With PHPUnit Slide 34 Fighting Fear-Driven-Development With PHPUnit Slide 35 Fighting Fear-Driven-Development With PHPUnit Slide 36 Fighting Fear-Driven-Development With PHPUnit Slide 37 Fighting Fear-Driven-Development With PHPUnit Slide 38 Fighting Fear-Driven-Development With PHPUnit Slide 39 Fighting Fear-Driven-Development With PHPUnit Slide 40 Fighting Fear-Driven-Development With PHPUnit Slide 41 Fighting Fear-Driven-Development With PHPUnit Slide 42 Fighting Fear-Driven-Development With PHPUnit Slide 43 Fighting Fear-Driven-Development With PHPUnit Slide 44 Fighting Fear-Driven-Development With PHPUnit Slide 45 Fighting Fear-Driven-Development With PHPUnit Slide 46 Fighting Fear-Driven-Development With PHPUnit Slide 47 Fighting Fear-Driven-Development With PHPUnit Slide 48 Fighting Fear-Driven-Development With PHPUnit Slide 49 Fighting Fear-Driven-Development With PHPUnit Slide 50 Fighting Fear-Driven-Development With PHPUnit Slide 51 Fighting Fear-Driven-Development With PHPUnit Slide 52 Fighting Fear-Driven-Development With PHPUnit Slide 53 Fighting Fear-Driven-Development With PHPUnit Slide 54 Fighting Fear-Driven-Development With PHPUnit Slide 55 Fighting Fear-Driven-Development With PHPUnit Slide 56
Upcoming SlideShare
Nginx pres
Next
Download to read offline and view in fullscreen.

5 Likes

Share

Download to read offline

Fighting Fear-Driven-Development With PHPUnit

Download to read offline

This talk was designed for PHP developers with limited or no experience in unit testing. I focus on describing the problem of fear-driven-development, and how test-driven-development can be used to improve the quality of your code.

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Fighting Fear-Driven-Development With PHPUnit

  1. 1. South Florida PHP Meetup Group
  2. 2.  James Fuller  Web Developer  http://www.jblotus.com  Started unit testing in response to a new job developing on a large, buggy legacy application used by millions
  3. 3.  Afraid to make even tiny code changes due to unpredictable side-effects?  Been burned by bugs that came back to life?  Does sketchy code keep you up at night?
  4. 4. Imagine your code as a dark twisted forest that you must navigate. Nothing makes sense, there are no clear paths and you are surrounded by bugs. Lots of bugs. How would you handle being stuck in this situation?
  5. 5.  Some will be brave and foolish, cutting corners to move quickly through the forest.  Some will be paralyzed by the thought of making the wrong move.  Most will succumb to the bugs or die a horrible death trying to find a way out.
  6. 6. Modify code Broke something Run feature God I hope Doesn't work this works Modify Code
  7. 7.  Write tests  Use PHPUnit to automate your tests  Practice Test-Driven-Development  Improve your design  Enjoy the added value
  8. 8.  Can you prove it works? automatically?  Can you change it and know it still works? without loading the browser?  Can you promise that if it breaks, it will never break like that again?  What about edge cases? You do care about edge cases, right?
  9. 9.  All developers practice some form of testing. Most PHP developers do it in the browser.  What we want to do is automate testing with PHPUnit.  Don't confuse apathy with laziness. Good coders automate repetitive tasks because they are lazy.
  10. 10.  Before you write new code  When you fix a bug  Before you begin refactoring code  As you go, improving code coverage We can use Test-Driven-Development to help us maintain disclipline in writing our unit tests.
  11. 11.  The principle of unit testing is to verify that a unit of code will function correctly in isolation.  A unit is the smallest testable part of an application, usually a function or method.
  12. 12. Understand Requirements Refactor Write tests Run Tests Run Tests (PASS) (FAIL) Write Code
  13. 13.  Focus on design of system before implementation  Changes are easier to make, with less side effects  Documentation via example  Less FEAR, more CONFIDENCE  You have tests at the end!
  14. 14.  PHPUnit is an automated testing framework PHP development based upon xUnit (java).  PHPUnit gives us an easy way to write tests, generate code coverage and improve the quality of our code.
  15. 15. Command line test runner Mature and expansive assertion library Code coverage Mock objects Lots of extensions Fast & Configurable
  16. 16.  PEAR  best for system wide access to phpunit  hard to downgrade or change versions  dependencies can be tricky  Github  allows the most control  easy to pack up in your version control system  lots of steps  Phar (PHP Archive)  a single phar can be easily included in your projects  in development  many features don't work or don't work as expected
  17. 17.  Upgrade PEAR to the latest version  sudo pear upgrade PEAR  Install PHPUnit  pear config-set auto_discover 1  pear install pear.phpunit.de/PHPUnit  This will install the latest release of PHPUnit.  Installing older versions is a bit trickier.
  18. 18.  pear install phpunit/DbUnit  pear install phpunit/PHPUnit_Selenium  pear install phpunit/PHPUnit_Story  pear install phpunit/PHPUnit_TestListener_DBUS  pear install phpunit/PHPUnit_TestListener_DBUS  pear install phpunit/PHPUnit_TestListener_XHProf  pear install phpunit/PHPUnit_TicketListener_Fogbugz  pear install phpunit/PHPUnit_TicketListener_GitHub  pear install phpunit/PHPUnit_TicketListener_GoogleCode  pear install phpunit/PHPUnit_TicketListener_Trac  pear install phpunit/PHP_Invoker
  19. 19.  Checkout source from github mkdir phpunit && cd phpunit git clone git://github.com/sebastianbergmann/phpunit.git git clone git://github.com/sebastianbergmann/dbunit.git …  Still need PEAR to install YAML component pear install pear.symfony-project.com/YAML  Make sure to add all these directories to your include_path  Checking out specific versions of packages: cd phpunit git checkout 3.6
  20. 20.  Usage  phpunit [switches] UnitTest [UnitTest.php]  phpunit [switches] <directory>  Common Switches  --stop-on-error Stop upon first error.  --stop-on-failure Stop upon first error or failure.  --debug Output debug info (test name)  -v|--verbose Output verbose information.  --bootstrap <file> Load this file before tests  --configuration <file> Specify config file (default phpunix.xml)
  21. 21.  PHPUnit runtime options can be defined using command line switches and/or xml (phpunit.xml)  Bootstrap your application as needed before tests run (bootstrap.php)  Define paths for code coverage output, logs, etc.
  22. 22. It's easy!! Include the autoloader in your bootstrap or in your test cases: <?php require_once 'PHPUnit/Autoload.php';
  23. 23.  A test suite contains any number of test cases.  A test case is a class that contains individual tests.  A test is a method that contains the code to test something.
  24. 24. / lib/ Foo.php tests/ suite/ lib/ FooTest.php
  25. 25. <phpunit> <testsuites> <testsuite name="All Tests"> <directory>tests/suite</directory> </testsuite> <testsuite name="Some Tests"> <file>tests/suite/FooTest.php</file> <file>tests/suite/BarTest.php</file> </testsuite> </testsuites> </phpunit>
  26. 26. A Fixture represents the known state of the world.  Object instantiation  Database Records  Preconditions & Postconditions PHPUnit offers several built-in methods to reduce the clutter of setting up fixtures.
  27. 27. class FooTest extends PHPUnit_Framework_TestCase { //runs before each test method protected function setUp() {} //runs after each test method protected function tearDown() {} //runs before any tests public static function setUpBeforeClass() {} //runs after all tests public static function tearDownAfterClass() {} }
  28. 28. class FooTest extends PHPUnit_Framework_TestCase { protected $Foo; protected function setUp() { $this->Foo = new Foo; } public function testFoo () { $this->assertNull($this->Foo->method()); } public function testFoo1() { $this->assertNull($this->Foo->otherMethod()); } }
  29. 29. //method being tested in class Foo public function reverseConcat($a, $b) { return strrev($a . $b); } //test method in class FooTest public function testReverseConcat() { $sut = new Foo; $actual = $sut->reverseConcat('Foo', 'Bar'); $expected = 'raBooF'; $this->assertEquals($expected, $actual); }
  30. 30.  An assertion is how we declare an expectation.  Well-tested systems rely upon verification, not developer intuition.  Things we can assert with PHPUnit  return values  certain methods/objects were called via Test Doubles  echo/print output
  31. 31. assertArrayHasKey() assertNull() assertClassHasAttribute() assertObjectHasAttribute() assertClassHasStaticAttribute() assertRegExp() assertContains() assertStringMatchesFormat() assertContainsOnly() assertStringMatchesFormatFile() assertCount() assertSame() assertEmpty() assertSelectCount() assertEqualXMLStructure() assertSelectEquals() assertEquals() assertSelectRegExp() assertFalse() assertStringEndsWith() assertFileEquals() assertStringEqualsFile() assertFileExists() assertStringStartsWith() assertGreaterThan() assertTag() assertGreaterThanOrEqual() assertThat() assertInstanceOf() assertTrue() assertInternalType() assertXmlFileEqualsXmlFile() assertLessThan() assertXmlStringEqualsXmlFile() assertLessThanOrEqual() assertXmlStringEqualsXmlString()
  32. 32. When assertions evaluate to TRUE the test will emit a PASS $this->assertTrue(true); $this->assertFalse(false); $this->assertEquals('foo', 'foo'); $this->assertEmpty(array()); $this->assertNotIsSame(new stdClass, new stdClass); $this->assertArrayHasKey('foo', array('foo' => 'bar')); $this->assertCount(2, array('apple', 'orange'));
  33. 33. When assertions evaluate to FALSE the test will emit a FAILURE $this->assertTrue(false); $this->assertFalse(true); $this->assertEquals('foo', 'bar'); $this->assertEmpty(array('foo')); $this->assertIsSame(new stdClass, new stdClass); $this->assertArrayHasKey('foo', array()); $this->assertCount(2, array('apple'));
  34. 34.  game has 2 players  players pick either rock, paper, or scissors  rock beats scissors  paper beats rock  scissors beats paper  round is a draw if both players pick the same object
  35. 35.  Isolation means that a unit of code is not affected by external factors.  In practice we can't always avoid external dependencies, but we benefit from trying to reduce their impact on our tests.  This means we need to be able to substitute external dependencies with test doubles.
  36. 36.  Test doubles are objects we use to substitute dependencies in our system under test.  Stubs return prebaked values when it's methods are called.  Mocks are like programmable stubs. You can verify that methods are called correctly and perform more complex verification.
  37. 37.  PHPUnit provides a built in mocking framework.  You can easily create test doubles that you can inject into your system under test.  This allows you to swap out complex dependencies with controllable and verifiable objects.
  38. 38. public function testStub() { $stub = $this->getMock('Stub', array('foo')); $stub->expects($this->once()) ->method('foo') ->will($this->returnValue('bar')); $actual = $stub->foo(); $this->assertEquals('bar', $actual, "should have returned 'bar'"); }
  39. 39.  Two ways to generate mock objects  getMock() with arguments  Mock Builder API
  40. 40. $this->getMock( $class_name, $mocked_methods, $constructor_args, $mock_class_name, $call_original_constructor, $call_original_clone_constructor, $disable_autoload );
  41. 41. $this->getMockBuilder($class_name) ->setMethods(array $methods) ->disableOriginalConstructor() ->disableOriginalClone() ->setConstructorArgs(array $args) ->setMockClassName($class_name) ->getMock();
  42. 42. $mock = $this->getMockBuilder('Foo') ->setMethods(array('someMethod')) ->getMock(); $mock->expects($this->any()) ->method('someMethod') ->will( $this->onConsecutiveCalls('bar', 'baz') ); $mock->someMethod(); //returns 'bar' $mock->someMethod(); //returns 'baz'
  43. 43. $mock = $this->getMockBuilder('Foo') ->setMethods(array('someMethod')) ->getMock(); $mock->expects($this->any()) ->method('someMethod') ->will($this->throwException( new RuntimeException )); //throws a RuntimeException $mock->someMethod();
  44. 44. class WebService { protected $Http; public function __construct(Http $http) { $this->Http = $http; } public function getUrl($url = '') { return $this->Http->get($url); } }
  45. 45. require_once 'PHPUnit/Autoload.php'; require_once 'WebService.php'; class TestWebService extends PHPUnit_Framework_TestCase { public function testGetUrl_callsHttpCorrectly() { $http = $this->getMockBuilder('FakeClassName') ->setMethods(array('get')) ->setMockClassName('Http') ->getMock(); $http->expects($this->once()) ->method('get') ->with($url); //inject the mock via constructor $sut = new WebService($http); //test Http get $sut->getUrl('http://google.com'); } }
  46. 46.  Static method calls  Instantiating objects directly (hard coupling)  External resources (network, file system)  Globals ($_GLOBALS, Singletons)
  47. 47. class Dependency { } class SystemUnderTest { public function badIdea() { $dependency = new Dependency; } }
  48. 48. class TwitterClient { function getTweets() { $url = 'api.twitter.com/v2/tweets/...'; return Http::consume($url); } }
  49. 49. class TwitterClient { protected $Http; public function __construct(Http $http) { $this->Http = $http; } public function getTweets() { $url = 'api.twitter.com/v2/tweets/...'; return $this->Http->consume($url); } }
  50. 50. class TestTwitterClient extends PHPUnit_Framework_TestCase { public function testCantMockTwitter() { $mock = $this->getMockBuilder(Http') ->setMethods(array('consume')) ->getMock(); $sut = new TwitterClient($mock); $sut->getTweets(); } }
  51. 51. class Singleton { public $data; protected static $instance; private function __construct() {} private function __clone() {} public function getInstance() { if (!self::$instance) { self::$instance = new self; } return self::$instance; } }
  52. 52. class SystemUnderTest { public function setData($data) { Singleton::getInstance()->data = $data; } public function getData() { return Singleton::getInstance()->data; } }
  53. 53. class TestSystemUnderTest extends PHPUnit_Framework_TestCase { public function testSingletonsAreFineAtFirst() { $sut = new SystemUnderTest; $sut->setData('foo'); //passes $this->assertEquals('foo', $sut->getData()); } public function testSingletonsNeverDie() { $sut = new SystemUnderTest; $actual = $sut->getData(); //fails $this->assertNull($actual); } }
  54. 54.  Many frameworks come with built in testing facilities, or the ability to easily integrate with PHPUnit.  Try to use the built-in testing tools. They usually do the heavy lifting of bootstrapping your application.
  55. 55.  Get your test harness working  Get your feet wet first, testing smaller/trivial code  Manual verification is prudent  Improve your design, refactor  Write tests for new code  Write tests opportunistically
  • mythii

    Mar. 27, 2015
  • mahtonu

    Mar. 23, 2014
  • mrprompt

    Jun. 10, 2013
  • rjustino

    May. 31, 2013
  • damiansromek

    Apr. 14, 2012

This talk was designed for PHP developers with limited or no experience in unit testing. I focus on describing the problem of fear-driven-development, and how test-driven-development can be used to improve the quality of your code.

Views

Total views

5,231

On Slideshare

0

From embeds

0

Number of embeds

484

Actions

Downloads

49

Shares

0

Comments

0

Likes

5

×