Your SlideShare is downloading. ×
Fighting Fear-Driven-Development With PHPUnit
Upcoming SlideShare
Loading in...5

Thanks for flagging this SlideShare!

Oops! An error has occurred.


Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Fighting Fear-Driven-Development With PHPUnit


Published on

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 …

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.

Published in: Technology

  • Be the first to comment

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

No notes for slide


  • 1. South Florida PHP Meetup Group
  • 2.  James Fuller Web Developer Started unit testing in response to a new job developing on a large, buggy legacy application used by millions
  • 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. Imagine your code as a dark twisted forest thatyou must navigate.Nothing makes sense, there are no clear pathsand you are surrounded by bugs. Lots of bugs.How would you handle being stuck in thissituation?
  • 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. Modify code Brokesomething Run featureGod I hope Doesnt workthis works Modify Code
  • 7.  Write tests Use PHPUnit to automate your tests Practice Test-Driven-Development Improve your design Enjoy the added value
  • 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.  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. Dont confuse apathy with laziness. Good coders automate repetitive tasks because they are lazy.
  • 10.  Before you write new code When you fix a bug Before you begin refactoring code As you go, improving code coverageWe can use Test-Driven-Development to helpus maintain disclipline in writing our unit tests.
  • 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. Understand RequirementsRefactor Write testsRun Tests Run Tests (PASS) (FAIL) Write Code
  • 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.  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. Command line test runnerMature and expansive assertion libraryCode coverageMock objectsLots of extensionsFast & Configurable
  • 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 dont work or dont work as expected
  • 17.  Upgrade PEAR to the latest version  sudo pear upgrade PEAR Install PHPUnit  pear config-set auto_discover 1  pear install This will install the latest release of PHPUnit. Installing older versions is a bit trickier.
  • 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.  Checkout source from github mkdir phpunit && cd phpunit git clone git:// git clone git:// … Still need PEAR to install YAML component pear install Make sure to add all these directories to your include_path Checking out specific versions of packages: cd phpunit git checkout 3.6
  • 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.  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. Its easy!! Include the autoloader in yourbootstrap or in your test cases:<?phprequire_once PHPUnit/Autoload.php;
  • 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. / lib/ Foo.php tests/ suite/ lib/ FooTest.php
  • 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. A Fixture represents the known state of theworld. Object instantiation Database Records Preconditions & PostconditionsPHPUnit offers several built-in methods toreduce the clutter of setting up fixtures.
  • 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. 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. //method being tested in class Foopublic function reverseConcat($a, $b) { return strrev($a . $b);}//test method in class FooTestpublic function testReverseConcat() { $sut = new Foo; $actual = $sut->reverseConcat(Foo, Bar); $expected = raBooF; $this->assertEquals($expected, $actual);}
  • 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. 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. 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. 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.  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.  Isolation means that a unit of code is not affected by external factors. In practice we cant 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.  Test doubles are objects we use to substitute dependencies in our system under test. Stubs return prebaked values when its methods are called. Mocks are like programmable stubs. You can verify that methods are called correctly and perform more complex verification.
  • 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. 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, "shouldhave returned bar");}
  • 39.  Two ways to generate mock objects getMock() with arguments Mock Builder API
  • 40. $this->getMock( $class_name, $mocked_methods, $constructor_args, $mock_class_name, $call_original_constructor, $call_original_clone_constructor, $disable_autoload);
  • 41. $this->getMockBuilder($class_name) ->setMethods(array $methods) ->disableOriginalConstructor() ->disableOriginalClone() ->setConstructorArgs(array $args) ->setMockClassName($class_name) ->getMock();
  • 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. $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. class WebService { protected $Http; public function __construct(Http $http) { $this->Http = $http; } public function getUrl($url = ) { return $this->Http->get($url); }}
  • 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(; }}
  • 46.  Static method calls Instantiating objects directly (hard coupling) External resources (network, file system) Globals ($_GLOBALS, Singletons)
  • 47. class Dependency {}class SystemUnderTest { public function badIdea() { $dependency = new Dependency; }}
  • 48. class TwitterClient { function getTweets() { $url =; return Http::consume($url); }}
  • 49. class TwitterClient { protected $Http; public function __construct(Http $http) { $this->Http = $http; } public function getTweets() { $url =; return $this->Http->consume($url); }}
  • 50. class TestTwitterClient extendsPHPUnit_Framework_TestCase { public function testCantMockTwitter() { $mock = $this->getMockBuilder(Http) ->setMethods(array(consume)) ->getMock(); $sut = new TwitterClient($mock); $sut->getTweets(); }}
  • 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. class SystemUnderTest { public function setData($data) { Singleton::getInstance()->data = $data; } public function getData() { return Singleton::getInstance()->data; }}
  • 53. class TestSystemUnderTest extendsPHPUnit_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.  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.  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