SlideShare a Scribd company logo
QUALITY ASSURANCE IN
                  MAGENTO EXTENSIONS
                             Automated Functional Testing




Wednesday, 9 February 2011
WHO AM I
    • Alistair           Stead

    • Technical Team             Lead @ Ibuildings UK / Session Digital

    • Lead           Magento projects for 3663, WMI, Kookai and others

    • Over            3 years experience with Magento

    • Zend            Certified Engineer

    • Over            11 years commercial experience developing in PHP


Wednesday, 9 February 2011
CREATING SOFTWARE IS
                        COMPLEX



Wednesday, 9 February 2011
CREATING SOFTWARE IS
                        COMPLEX
                                       ing it is argua bly harder)
                             (Maintain




Wednesday, 9 February 2011
FAULT TOLERANCE IS

                                  LOW


Wednesday, 9 February 2011
“Hello, technical support?”




                                                           “I would like
                                                            to report a bug...”

Wednesday, 9 February 2011
E-COMMERCE HAS A LOW
              TOLERANCE TO FAULTS.




Wednesday, 9 February 2011
MAGENTO MAKES IT
                  OBVIOUS SOMETHING IS
                        WRONG




Wednesday, 9 February 2011
With whom should
                                   the BUCK stop?




Wednesday, 9 February 2011
With whom should
                                   the BUCK stop?




           e sure its not us....
   Lets mak
Wednesday, 9 February 2011
SO WHAT CAN WE DO TO
                  FIX THIS?



Wednesday, 9 February 2011
We are clever
                                     people right?
Wednesday, 9 February 2011
Just get more of us?




Wednesday, 9 February 2011
Testing at the eleventh hour...


Wednesday, 9 February 2011
We can use
                             technology
                               to help!




Wednesday, 9 February 2011
AUTOMATED TESTING
        FRAMEWORKS CAN DO THE
             HEAVY LIFTING.



                             Selenium   Watir



Wednesday, 9 February 2011
PHPUNIT
                 The de-facto standard for unit testing in PHP projects




Wednesday, 9 February 2011
WE CAN RUN UNIT TESTS
                  FOR MAGENTO!




Wednesday, 9 February 2011
<?php
 /**
  * Magento PHPUnit TestCase
  *
  * @package     Ibuildings_Mage_Test_PHPUnit
  * @copyright   Copyright (c) 2011 Ibuildings. (http://www.ibuildings.com)
  * @license     http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  * @author      Alistair Stead <alistair@ibuildings.com>
  * @version     $Id$
  */

 /**
  * PHPUnit_Framework_Magento_TestCase
  *
  * @category    Mage_Test
  * @package     Ibuildings_Mage_Test_PHPUnit
  * @subpackage Ibuildings_Mage_Test_PHPUnit_TestCase
  * @uses        PHPUnit_Framework_TestCase
  */
 abstract class Ibuildings_Mage_Test_PHPUnit_TestCase extends PHPUnit_Framework_TestCase {

       public function setUp() {
           parent::setUp();
           // Initialise Magento
           Mage::app();
       }
 }




Wednesday, 9 February 2011
<?php
 /**
  * Magento PHPUnit TestCase
  *
  * @package     Ibuildings_Mage_Test_PHPUnit
  * @copyright   Copyright (c) 2011 Ibuildings. (http://www.ibuildings.com)
  * @license     http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  * @author      Alistair Stead <alistair@ibuildings.com>
  * @version     $Id$
  */

 /**
  * PHPUnit_Framework_Magento_TestCase
  *
  * @category    Mage_Test
  * @package     Ibuildings_Mage_Test_PHPUnit
  * @subpackage Ibuildings_Mage_Test_PHPUnit_TestCase
  * @uses        PHPUnit_Framework_TestCase
  */
 abstract class Ibuildings_Mage_Test_PHPUnit_TestCase extends PHPUnit_Framework_TestCase {

       public function setUp() {
           parent::setUp();
           // Initialise Magento
           Mage::app();
       }
 }




Wednesday, 9 February 2011
MODELS




Wednesday, 9 February 2011
<?php

 class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase
 {
     /**
      * Member variable to hold reference to the opbject under test
      *
      * @var DataCash_Dpg_Model_Config
      **/
     protected $_object;

        /**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_object = Mage::getSingleton('dpg/config');
        }




Wednesday, 9 February 2011
<?php

 class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase
 {
     /**
      * Member variable to hold reference to the opbject under test
      *
      * @var DataCash_Dpg_Model_Config
      **/
     protected $_object;

        /**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_object = Mage::getSingleton('dpg/config');
        }




Wednesday, 9 February 2011
<?php

 class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase
 {
     /**
      * Member variable to hold reference to the opbject under test
      *
      * @var DataCash_Dpg_Model_Config
      **/
     protected $_object;

        /**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_object = Mage::getSingleton('dpg/config');
        }




Wednesday, 9 February 2011
/**
       * mageSingletonFactoryReturnsExpectedObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsExpectedObject()
     {
          $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object
 returned by factory');
     } // mageSingletonFactoryReturnsExpectedObject

     /**
       * mageSingletonFactoryReturnsTheSameObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsTheSameObject()
     {
          $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects
 have been created');
     } // mageSingletonFactoryReturnsTheSameObject




Wednesday, 9 February 2011
/**
       * mageSingletonFactoryReturnsExpectedObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsExpectedObject()
     {
          $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object
 returned by factory');
     } // mageSingletonFactoryReturnsExpectedObject

     /**
       * mageSingletonFactoryReturnsTheSameObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsTheSameObject()
     {
          $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects
 have been created');
     } // mageSingletonFactoryReturnsTheSameObject




Wednesday, 9 February 2011
/**
       * mageSingletonFactoryReturnsExpectedObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsExpectedObject()
     {
          $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object
 returned by factory');
     } // mageSingletonFactoryReturnsExpectedObject

     /**
       * mageSingletonFactoryReturnsTheSameObject
       * @author Alistair Stead
       * @test
       */
     public function mageSingletonFactoryReturnsTheSameObject()
     {
          $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects
 have been created');
     } // mageSingletonFactoryReturnsTheSameObject




Wednesday, 9 February 2011
/**
        * getTransactionTypesReturnsArray
        * @author Alistair Stead
        * @test
        */
      public function getTransactionTypesReturnsArray()
      {
           $result = $this->_object->getTansactionTypes();
           $this->assertTrue(is_array($result), 'No array returned');
           $this->assertEquals(count($result), 2, 'More transaction types than expected');
           $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' );
           $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' );
      } // getTransactionTypesReturnsArray




Wednesday, 9 February 2011
/**
        * getTransactionTypesReturnsArray
        * @author Alistair Stead
        * @test
        */
      public function getTransactionTypesReturnsArray()
      {
           $result = $this->_object->getTansactionTypes();
           $this->assertTrue(is_array($result), 'No array returned');
           $this->assertEquals(count($result), 2, 'More transaction types than expected');
           $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' );
           $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' );
      } // getTransactionTypesReturnsArray




Wednesday, 9 February 2011
HELPERS




Wednesday, 9 February 2011
/**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_helper = Mage::helper('contactsdepartment');
              $this->_departmentIds = array();
              $department = Mage::getModel('contactsdepartment/department');
              $fixtures = array(
                  array(
                      'department_name' => 'department one',
                      'department_email' => 'email1@department.com',
                      'store_id' => 0
                  )
              );

              foreach ($fixtures as $data) {
                  $department->addData($data);
                  $department->save();

                     $this->_departmentIds[] = $department->getId();
              }
        }




Wednesday, 9 February 2011
/**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_helper = Mage::helper('contactsdepartment');
              $this->_departmentIds = array();
              $department = Mage::getModel('contactsdepartment/department');
              $fixtures = array(
                  array(
                      'department_name' => 'department one',
                      'department_email' => 'email1@department.com',
                      'store_id' => 0
                  )
              );

              foreach ($fixtures as $data) {
                  $department->addData($data);
                  $department->save();

                     $this->_departmentIds[] = $department->getId();
              }
        }




Wednesday, 9 February 2011
/**
          * Setup fixtures and dependencies
          *
          * @return void
          * @author Alistair Stead
          **/
        public function setUp()
        {
              parent::setUp();
              $this->_helper = Mage::helper('contactsdepartment');
              $this->_departmentIds = array();
              $department = Mage::getModel('contactsdepartment/department');
              $fixtures = array(
                  array(
                      'department_name' => 'department one',
                      'department_email' => 'email1@department.com',
                      'store_id' => 0
                  )
              );

              foreach ($fixtures as $data) {
                  $department->addData($data);
                  $department->save();

                     $this->_departmentIds[] = $department->getId();
              }
        }




Wednesday, 9 February 2011
/**
         * helperFactoryReturnsTheExpectedClass
         * @author Alistair Stead
         * @test
         */
       public function helperFactoryReturnsTheExpectedClass()
       {
            $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper);
       } // helperFactoryReturnsTheExpectedClass

       /**
         * getDepartmentOptionsShouldReturnAssociativeArray
         * @author Alistair Stead
         * @test
         */
       public function getDepartmentOptionsShouldReturnAssociativeArray()
       {
            foreach ($this->_helper->getDepartmentOptions() as $option) {
                 $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' );
                 $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' );
            }
       } // getDepartmentOptionsShouldReturnAssociativeArray




Wednesday, 9 February 2011
/**
         * helperFactoryReturnsTheExpectedClass
         * @author Alistair Stead
         * @test
         */
       public function helperFactoryReturnsTheExpectedClass()
       {
            $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper);
       } // helperFactoryReturnsTheExpectedClass

       /**
         * getDepartmentOptionsShouldReturnAssociativeArray
         * @author Alistair Stead
         * @test
         */
       public function getDepartmentOptionsShouldReturnAssociativeArray()
       {
            foreach ($this->_helper->getDepartmentOptions() as $option) {
                 $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' );
                 $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' );
            }
       } // getDepartmentOptionsShouldReturnAssociativeArray




Wednesday, 9 February 2011
/**
         * helperFactoryReturnsTheExpectedClass
         * @author Alistair Stead
         * @test
         */
       public function helperFactoryReturnsTheExpectedClass()
       {
            $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper);
       } // helperFactoryReturnsTheExpectedClass

       /**
         * getDepartmentOptionsShouldReturnAssociativeArray
         * @author Alistair Stead
         * @test
         */
       public function getDepartmentOptionsShouldReturnAssociativeArray()
       {
            foreach ($this->_helper->getDepartmentOptions() as $option) {
                 $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' );
                 $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' );
            }
       } // getDepartmentOptionsShouldReturnAssociativeArray




Wednesday, 9 February 2011
Okay we’re done lets go home...




Wednesday, 9 February 2011
Hold on,
                              we have not really
                             done anything new.
Wednesday, 9 February 2011
DID WE MENTION
           FUNCTIONAL TESTING YET?



Wednesday, 9 February 2011
QUALITY ASSURANCE IN
                  MAGENTO EXTENSIONS
                             Automated Functional Testing




Wednesday, 9 February 2011
ZEND_TEST?




Wednesday, 9 February 2011
MAGENTO IS CLOSELY
               COUPLED TO
          MAGE_CORE_APPLICATION



Wednesday, 9 February 2011
Coupled components cause inflexibility
Wednesday, 9 February 2011
Dependency injection
                                           is the key

Wednesday, 9 February 2011
MAGE-TEST
                             http://github.com/ibuildings/Mage-Test




Wednesday, 9 February 2011
Mage_Core_Model_App


                      Ibuildings_Mage_Controller_Request_HttpTestCase


                    Ibuildings_Mage_Controller_Response_HttpTestCase



                      Ibuildings_Mage_Test_PHPUnit_ControllerTestCase


                             Ibuildings_Mage_Test_PHPUnit_TestCase


Wednesday, 9 February 2011
MAGE_CORE_APPLICATION
                             Loaded from the community code pool




Wednesday, 9 February 2011
/**
         * Provide a public method to allow the internal Request object
         * to be set at runtime. This can be used to inject a testing request object
         *
         * @return void
         * @author Alistair Stead
         **/
       public function setRequest(Zend_Controller_Request_Abstract $request)
       {
             $this->_request = $request;
       }

       /**
         * Retrieve request object
         *
         * @return Mage_Core_Controller_Request_Http
         */
       public function getRequest()
       {
            if (empty($this->_request)) {
                $this->_request = new Mage_Core_Controller_Request_Http();
            }
            return $this->_request;
       }




Wednesday, 9 February 2011
/**
         * Provide a public method to allow the internal Request object
         * to be set at runtime. This can be used to inject a testing request object
         *
         * @return void
         * @author Alistair Stead
         **/
       public function setRequest(Zend_Controller_Request_Abstract $request)
       {
             $this->_request = $request;
       }

       /**
         * Retrieve request object
         *
         * @return Mage_Core_Controller_Request_Http
         */
       public function getRequest()
       {
            if (empty($this->_request)) {
                $this->_request = new Mage_Core_Controller_Request_Http();
            }
            return $this->_request;
       }




Wednesday, 9 February 2011
/**
        * Provide a public method to allow the protected internal Response object
        * to be set at runtime. This can be used to inject a testing response object
        *
        * @return void
        * @author Alistair Stead
        **/
      public function setResponse(Zend_Controller_Response_Abstract $response)
      {
            $this->_response = $response;
      }

      /**
        * Retrieve response object
        *
        * @return Zend_Controller_Response_Http
        */
      public function getResponse()
      {
           if (empty($this->_response)) {
               $this->_response = new Mage_Core_Controller_Response_Http();
               $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException;
               $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8");
           }
           return $this->_response;
      }




Wednesday, 9 February 2011
/**
        * Provide a public method to allow the protected internal Response object
        * to be set at runtime. This can be used to inject a testing response object
        *
        * @return void
        * @author Alistair Stead
        **/
      public function setResponse(Zend_Controller_Response_Abstract $response)
      {
            $this->_response = $response;
      }

      /**
        * Retrieve response object
        *
        * @return Zend_Controller_Response_Http
        */
      public function getResponse()
      {
           if (empty($this->_response)) {
               $this->_response = new Mage_Core_Controller_Response_Http();
               $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException;
               $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8");
           }
           return $this->_response;
      }




Wednesday, 9 February 2011
CONTROLLER TESTS
                   Ibuildings_Mage_Test_PHPUnit_ControllerTestCase




Wednesday, 9 February 2011
/**
   * Bootstrap the Mage application in a similar way to the procedure
   * of index.php
   *
   * Then sets test case request and response objects in Mage_Core_App,
   * and disables returning the response.
   *
   * @return void
   * @author Alistair Stead
   */
 public function mageBootstrap()
 {
      Mage::reset();
      if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
          Mage::setIsDeveloperMode(true);
      }
      // Store or website code
      $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

        // Run store or run website
        $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

        // Initialize the Mage App and inject the testing request & response
        Mage::app($this->mageRunCode, $this->mageRunType, $this->options);
        Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase);
        Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase);
 }




Wednesday, 9 February 2011
/**
   * Bootstrap the Mage application in a similar way to the procedure
   * of index.php
   *
   * Then sets test case request and response objects in Mage_Core_App,
   * and disables returning the response.
   *
   * @return void
   * @author Alistair Stead
   */
 public function mageBootstrap()
 {
      Mage::reset();
      if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
          Mage::setIsDeveloperMode(true);
      }
      // Store or website code
      $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

        // Run store or run website
        $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

        // Initialize the Mage App and inject the testing request & response
        Mage::app($this->mageRunCode, $this->mageRunType, $this->options);
        Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase);
        Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase);
 }




Wednesday, 9 February 2011
/**
   * Bootstrap the Mage application in a similar way to the procedure
   * of index.php
   *
   * Then sets test case request and response objects in Mage_Core_App,
   * and disables returning the response.
   *
   * @return void
   * @author Alistair Stead
   */
 public function mageBootstrap()
 {
      Mage::reset();
      if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
          Mage::setIsDeveloperMode(true);
      }
      // Store or website code
      $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

        // Run store or run website
        $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

        // Initialize the Mage App and inject the testing request & response
        Mage::app($this->mageRunCode, $this->mageRunType, $this->options);
        Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase);
        Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase);
 }




Wednesday, 9 February 2011
Ibuildings_Mage_Controller_Request_HttpTestCase




                                                                                     Model
                                                                              View
                                                                 Controller



              Ibuildings_Mage_Controller_Response_HttpTestCase




Wednesday, 9 February 2011
Ibuildings_Mage_Controller_Request_HttpTestCase




                                                                                     Model
                                                                              View
                                                                 Controller



              Ibuildings_Mage_Controller_Response_HttpTestCase




Wednesday, 9 February 2011
Mage_Core_Model_App



               Ibuildings_Mage_Controller_Request_HttpTestCase




                                                                                     Model
                                                                              View
                                                                 Controller



              Ibuildings_Mage_Controller_Response_HttpTestCase




Wednesday, 9 February 2011
<?php

 /**
  * Mage_Catalog_IndexControllerTest
  *
  * @package    Mage_Catalog
  * @subpackage Mage_Catalog_Test
  *
  *
  * @uses PHPUnit_Framework_Magento_TestCase
  */
 class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {

       /**
         * theIndexActionShouldRedirectToRoot
         * @author Alistair Stead
         * @test
         */
       public function theIndexActionShouldRedirectToRoot()
       {
            $this->dispatch('/');

           $this->assertRoute('cms', "The expected cms route has not been matched");
           $this->assertAction('index', "The index action has not been called");
           $this->assertController('index', "The expected controller is not been used");
           $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page');
       } // theIndexActionShouldRedirectToRoot
 }




Wednesday, 9 February 2011
<?php

 /**
  * Mage_Catalog_IndexControllerTest
  *
  * @package    Mage_Catalog
  * @subpackage Mage_Catalog_Test
  *
  *
  * @uses PHPUnit_Framework_Magento_TestCase
  */
 class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {

       /**
         * theIndexActionShouldRedirectToRoot
         * @author Alistair Stead
         * @test
         */
       public function theIndexActionShouldRedirectToRoot()
       {
            $this->dispatch('/');

           $this->assertRoute('cms', "The expected cms route has not been matched");
           $this->assertAction('index', "The index action has not been called");
           $this->assertController('index', "The expected controller is not been used");
           $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page');
       } // theIndexActionShouldRedirectToRoot
 }




Wednesday, 9 February 2011
<?php

 /**
  * Mage_Catalog_IndexControllerTest
  *
  * @package    Mage_Catalog
  * @subpackage Mage_Catalog_Test
  *
  *
  * @uses PHPUnit_Framework_Magento_TestCase
  */
 class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {

       /**
         * theIndexActionShouldRedirectToRoot
         * @author Alistair Stead
         * @test
         */
       public function theIndexActionShouldRedirectToRoot()
       {
            $this->dispatch('/');

           $this->assertRoute('cms', "The expected cms route has not been matched");
           $this->assertAction('index', "The index action has not been called");
           $this->assertController('index', "The expected controller is not been used");
           $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page');
       } // theIndexActionShouldRedirectToRoot
 }




Wednesday, 9 February 2011
/**
         * theAdminRouteAccessesTheAdminApplicationArea
         * @author Alistair Stead
         * @test
         */
       public function theAdminRouteAccessesTheAdminApplicationArea()
       {
            $this->dispatch('admin/');

           $this->assertRoute('adminhtml', "The expected route has not been matched");
           $this->assertAction('login', "The login form should be presented");
           $this->assertController('index', "The expected controller is not been used");
       } // theAdminRouteAccessesTheAdminApplicationArea

       /**
         * theIndexActionDisplaysLoginForm
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function theIndexActionDisplaysLoginForm()
       {
            $this->dispatch('admin/index/');

           $this->assertQueryCount('form#loginForm', 1);
       } // theIndexActionDisplaysLoginForm




Wednesday, 9 February 2011
/**
         * theAdminRouteAccessesTheAdminApplicationArea
         * @author Alistair Stead
         * @test
         */
       public function theAdminRouteAccessesTheAdminApplicationArea()
       {
            $this->dispatch('admin/');

           $this->assertRoute('adminhtml', "The expected route has not been matched");
           $this->assertAction('login', "The login form should be presented");
           $this->assertController('index', "The expected controller is not been used");
       } // theAdminRouteAccessesTheAdminApplicationArea

       /**
         * theIndexActionDisplaysLoginForm
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function theIndexActionDisplaysLoginForm()
       {
            $this->dispatch('admin/index/');

           $this->assertQueryCount('form#loginForm', 1);
       } // theIndexActionDisplaysLoginForm




Wednesday, 9 February 2011
/**
         * theAdminRouteAccessesTheAdminApplicationArea
         * @author Alistair Stead
         * @test
         */
       public function theAdminRouteAccessesTheAdminApplicationArea()
       {
            $this->dispatch('admin/');

           $this->assertRoute('adminhtml', "The expected route has not been matched");
           $this->assertAction('login', "The login form should be presented");
           $this->assertController('index', "The expected controller is not been used");
       } // theAdminRouteAccessesTheAdminApplicationArea

       /**
         * theIndexActionDisplaysLoginForm
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function theIndexActionDisplaysLoginForm()
       {
            $this->dispatch('admin/index/');

           $this->assertQueryCount('form#loginForm', 1);
       } // theIndexActionDisplaysLoginForm




Wednesday, 9 February 2011
/**
         * theAdminRouteAccessesTheAdminApplicationArea
         * @author Alistair Stead
         * @test
         */
       public function theAdminRouteAccessesTheAdminApplicationArea()
       {
            $this->dispatch('admin/');

           $this->assertRoute('adminhtml', "The expected route has not been matched");
           $this->assertAction('login', "The login form should be presented");
           $this->assertController('index', "The expected controller is not been used");
       } // theAdminRouteAccessesTheAdminApplicationArea

       /**
         * theIndexActionDisplaysLoginForm
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function theIndexActionDisplaysLoginForm()
       {
            $this->dispatch('admin/index/');

           $this->assertQueryCount('form#loginForm', 1);
       } // theIndexActionDisplaysLoginForm




Wednesday, 9 February 2011
/**
         * theAdminRouteAccessesTheAdminApplicationArea
         * @author Alistair Stead
         * @test
         */
       public function theAdminRouteAccessesTheAdminApplicationArea()
       {
            $this->dispatch('admin/');

           $this->assertRoute('adminhtml', "The expected route has not been matched");
           $this->assertAction('login', "The login form should be presented");
           $this->assertController('index', "The expected controller is not been used");
       } // theAdminRouteAccessesTheAdminApplicationArea

       /**
         * theIndexActionDisplaysLoginForm
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function theIndexActionDisplaysLoginForm()
       {
            $this->dispatch('admin/index/');

           $this->assertQueryCount('form#loginForm', 1);
       } // theIndexActionDisplaysLoginForm




Wednesday, 9 February 2011
/**
         * submittingInvalidCredsShouldDisplayError
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function submittingInvalidCredsShouldDisplayError()
       {
            $this->request->setMethod('POST')
                 ->setPost(
                     array(
                         'login' => array(
                             'username' => 'admin',
                             'password' => 'invalid-password',
                         )
                     )
            );

              $this->dispatch('admin/index/login');

           $this->assertQueryCount('li.error-msg', 1);
           $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.');
       } // submittingInvalidCredsShouldDisplayError




Wednesday, 9 February 2011
/**
         * submittingInvalidCredsShouldDisplayError
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function submittingInvalidCredsShouldDisplayError()
       {
            $this->request->setMethod('POST')
                 ->setPost(
                     array(
                         'login' => array(
                             'username' => 'admin',
                             'password' => 'invalid-password',
                         )
                     )
            );

              $this->dispatch('admin/index/login');

           $this->assertQueryCount('li.error-msg', 1);
           $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.');
       } // submittingInvalidCredsShouldDisplayError




Wednesday, 9 February 2011
/**
         * submittingInvalidCredsShouldDisplayError
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function submittingInvalidCredsShouldDisplayError()
       {
            $this->request->setMethod('POST')
                 ->setPost(
                     array(
                         'login' => array(
                             'username' => 'admin',
                             'password' => 'invalid-password',
                         )
                     )
            );

              $this->dispatch('admin/index/login');

           $this->assertQueryCount('li.error-msg', 1);
           $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.');
       } // submittingInvalidCredsShouldDisplayError




Wednesday, 9 February 2011
/**
         * submittingInvalidCredsShouldDisplayError
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function submittingInvalidCredsShouldDisplayError()
       {
            $this->request->setMethod('POST')
                 ->setPost(
                     array(
                         'login' => array(
                             'username' => 'admin',
                             'password' => 'invalid-password',
                         )
                     )
            );

              $this->dispatch('admin/index/login');

           $this->assertQueryCount('li.error-msg', 1);
           $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.');
       } // submittingInvalidCredsShouldDisplayError




Wednesday, 9 February 2011
/**
         * submittingInvalidCredsShouldDisplayError
         * @author Alistair Stead
         * @group login
         * @test
         */
       public function submittingInvalidCredsShouldDisplayError()
       {
            $this->request->setMethod('POST')
                 ->setPost(
                     array(
                         'login' => array(
                             'username' => 'admin',
                             'password' => 'invalid-password',
                         )
                     )
            );

              $this->dispatch('admin/index/login');

           $this->assertQueryCount('li.error-msg', 1);
           $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.');
       } // submittingInvalidCredsShouldDisplayError




Wednesday, 9 February 2011
/**
        * submittingValidCredsShouldDisplayDashboard
        * @author Alistair Stead
        * @group login
        * @test
        */
      public function submittingValidCredsShouldDisplayDashboard()
      {
           $this->request->setMethod('POST')
                ->setPost(
                    array(
                        'login' => array(
                            'username' => 'admin',
                            'password' => '123456',
                        )
                    )
            );
            $this->dispatch('admin/index/login');

          $this->assertRedirect('We should be redirected after login');
          $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard');
      } // submittingValidCredsShouldDisplayDashboard




Wednesday, 9 February 2011
/**
        * submittingValidCredsShouldDisplayDashboard
        * @author Alistair Stead
        * @group login
        * @test
        */
      public function submittingValidCredsShouldDisplayDashboard()
      {
           $this->request->setMethod('POST')
                ->setPost(
                    array(
                        'login' => array(
                            'username' => 'admin',
                            'password' => '123456',
                        )
                    )
            );
            $this->dispatch('admin/index/login');

          $this->assertRedirect('We should be redirected after login');
          $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard');
      } // submittingValidCredsShouldDisplayDashboard




Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {

      /**
        * indexActionListsOrders
        * @author Alistair Stead
        * @test
        */
      public function indexActionListsOrders()
      {
           $this->request->setMethod('POST')
                ->setPost(
                    array(
                        'login' => array(
                            'username' => 'admin',
                            'password' => '123456',
                        )
                    )
           );
           $this->dispatch('admin/index/login');
           // Reset the requests after login before next dispatch
           $this->reset();

             $this->dispatch('admin/sales_order/index');

          $this->assertQueryContentContains('h3.icon-head', 'Orders');
      } // indexActionListsOrders

}




Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {

      /**
        * indexActionListsOrders
        * @author Alistair Stead
        * @test
        */
      public function indexActionListsOrders()
      {
           $this->request->setMethod('POST')
                ->setPost(
                    array(
                        'login' => array(
                            'username' => 'admin',
                            'password' => '123456',
                        )
                    )
           );
           $this->dispatch('admin/index/login');
           // Reset the requests after login before next dispatch
           $this->reset();

             $this->dispatch('admin/sales_order/index');

          $this->assertQueryContentContains('h3.icon-head', 'Orders');
      } // indexActionListsOrders

}




Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {

      /**
        * indexActionListsOrders
        * @author Alistair Stead
        * @test
        */
      public function indexActionListsOrders()
      {
           $this->request->setMethod('POST')
                ->setPost(
                    array(
                        'login' => array(
                            'username' => 'admin',
                            'password' => '123456',
                        )
                    )
           );
           $this->dispatch('admin/index/login');
           // Reset the requests after login before next dispatch
           $this->reset();

             $this->dispatch('admin/sales_order/index');

          $this->assertQueryContentContains('h3.icon-head', 'Orders');
      } // indexActionListsOrders

}




Wednesday, 9 February 2011
TESTING EMAIL CONTENT
                   Who gets all the emails sent to test@example.com?




Wednesday, 9 February 2011
public function mageBootstrap()
      {
          Mage::reset();
          if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
              Mage::setIsDeveloperMode(true);
          }
          // Store or website code
          $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

            // Run store or run website
            $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

            // Initialize the Mage App and inject the testing request & response
            Mage::app($this->mageRunCode, $this->mageRunType, $this->options);
            Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase);
            Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase);

        // Rewrite the core classes at runtime to prevent emails from being sent
        Mage::getConfig()->setNode('global/models/core/rewrite/email_template',
'Ibuildings_Test_Model_Email_Template');
        // This is a hack to get the runtime config changes to take effect
        Mage::getModel('core/email_template');
    }




Wednesday, 9 February 2011
public function mageBootstrap()
      {
          Mage::reset();
          if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
              Mage::setIsDeveloperMode(true);
          }
          // Store or website code
          $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

            // Run store or run website
            $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

            // Initialize the Mage App and inject the testing request & response
            Mage::app($this->mageRunCode, $this->mageRunType, $this->options);
            Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase);
            Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase);

        // Rewrite the core classes at runtime to prevent emails from being sent
        Mage::getConfig()->setNode('global/models/core/rewrite/email_template',
'Ibuildings_Test_Model_Email_Template');
        // This is a hack to get the runtime config changes to take effect
        Mage::getModel('core/email_template');
    }




Wednesday, 9 February 2011
/**
        * submittingForgotPasswordWithValidEmailReturnsSuccess
        * @author Alistair Stead
        * @group password
        * @test
        *
        */
      public function submittingForgotPasswordWithValidEmailReturnsSuccess()
      {
           $this->request->setMethod('POST')
                ->setPost(array('email' => $this->email));

            $this->dispatch('admin/index/forgotpassword/');

        $this->assertQueryCount('li.success-msg', 1);
        $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email
address. Please check your email and click Back to Login.');
        // Test that the email contains the correct data
        $emailContent = $this->getResponseEmail()
                            ->getBodyHtml()
                            ->getContent();
        // Overriding the response body to be able to use the standard content assertions
        $this->response->setBody($emailContent);

          // The email content addresses the fixture user
          $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName");
          // The fixture users password has been changed
          $this->assertNotQueryContentContains('body', $this->password);
      } // submittingForgotPasswordWithValidEmailReturnsSuccess




Wednesday, 9 February 2011
/**
        * submittingForgotPasswordWithValidEmailReturnsSuccess
        * @author Alistair Stead
        * @group password
        * @test
        *
        */
      public function submittingForgotPasswordWithValidEmailReturnsSuccess()
      {
           $this->request->setMethod('POST')
                ->setPost(array('email' => $this->email));

            $this->dispatch('admin/index/forgotpassword/');

        $this->assertQueryCount('li.success-msg', 1);
        $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email
address. Please check your email and click Back to Login.');
        // Test that the email contains the correct data
        $emailContent = $this->getResponseEmail()
                            ->getBodyHtml()
                            ->getContent();
        // Overriding the response body to be able to use the standard content assertions
        $this->response->setBody($emailContent);

          // The email content addresses the fixture user
          $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName");
          // The fixture users password has been changed
          $this->assertNotQueryContentContains('body', $this->password);
      } // submittingForgotPasswordWithValidEmailReturnsSuccess




Wednesday, 9 February 2011
/**
        * submittingForgotPasswordWithValidEmailReturnsSuccess
        * @author Alistair Stead
        * @group password
        * @test
        *
        */
      public function submittingForgotPasswordWithValidEmailReturnsSuccess()
      {
           $this->request->setMethod('POST')
                ->setPost(array('email' => $this->email));

            $this->dispatch('admin/index/forgotpassword/');

        $this->assertQueryCount('li.success-msg', 1);
        $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email
address. Please check your email and click Back to Login.');
        // Test that the email contains the correct data
        $emailContent = $this->getResponseEmail()
                            ->getBodyHtml()
                            ->getContent();
        // Overriding the response body to be able to use the standard content assertions
        $this->response->setBody($emailContent);

          // The email content addresses the fixture user
          $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName");
          // The fixture users password has been changed
          $this->assertNotQueryContentContains('body', $this->password);
      } // submittingForgotPasswordWithValidEmailReturnsSuccess




Wednesday, 9 February 2011
/**
        * submittingForgotPasswordWithValidEmailReturnsSuccess
        * @author Alistair Stead
        * @group password
        * @test
        *
        */
      public function submittingForgotPasswordWithValidEmailReturnsSuccess()
      {
           $this->request->setMethod('POST')
                ->setPost(array('email' => $this->email));

            $this->dispatch('admin/index/forgotpassword/');

        $this->assertQueryCount('li.success-msg', 1);
        $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email
address. Please check your email and click Back to Login.');
        // Test that the email contains the correct data
        $emailContent = $this->getResponseEmail()
                            ->getBodyHtml()
                            ->getContent();
        // Overriding the response body to be able to use the standard content assertions
        $this->response->setBody($emailContent);

          // The email content addresses the fixture user
          $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName");
          // The fixture users password has been changed
          $this->assertNotQueryContentContains('body', $this->password);
      } // submittingForgotPasswordWithValidEmailReturnsSuccess




Wednesday, 9 February 2011
SO WHAT DO AUTOMATED
              TESTS GIVE US?



Wednesday, 9 February 2011
CODE COVERAGE




Wednesday, 9 February 2011
STATIC CODE ANALYSIS




Wednesday, 9 February 2011
CONTINUOUS INTEGRATION




Wednesday, 9 February 2011
Wednesday, 9 February 2011
Wednesday, 9 February 2011
SO WHAT NOW?




Wednesday, 9 February 2011
MAGE-TEST IS OPEN SOURCE

    • Use          Mage-Test to test your development projects

    • Use          Mage-Test to test you extensions

    • Contribute             tests for Magento

    • Build          up the coverage of the Magento codebase

    • Take          advantage of core tests for regression testing


Wednesday, 9 February 2011
MAGE-TEST
                             http://github.com/ibuildings/Mage-Test




Wednesday, 9 February 2011
THANK YOU!


    • Email: astead@ibuildings.com

    • Skype: astead-ibuildings

    • Twitter: @alistairstead




Wednesday, 9 February 2011
REFERENCES

    • PHPUnit                https://github.com/sebastianbergmann/phpunit/

    • Zend_Test    http://framework.zend.com/manual/en/
       zend.test.html

    • Mage-Test               https://github.com/ibuildings/Mage-Test

    • phpUnderControl                 http://phpundercontrol.org/

    • Bamboo                 http://www.atlassian.com/software/bamboo/

Wednesday, 9 February 2011
IMAGE CREDITS

       BA Plane: http://www.flickr.com/photos/bribri/1299325208/sizes/l/in/photostream/

       Minions: http://www.akblessingsabound.com/wp-content/uploads/2010/06/despicable-me-minions-
       blessings-abound-mommy.jpg

       Eleventh Hour: http://www.flickr.com/photos/d4dee/2258343575/sizes/l/in/photostream/

       Deep Thought: http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/

       US Dollar: http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/

       Whoa (Stop Sign): http://www.flickr.com/photos/aquaoracle/3265987824/sizes/l/

       Departures Board: http://www.flickr.com/photos/scottmulhollan/4892422469/sizes/l/in/photostream/

       Syringe: http://www.flickr.com/photos/woodypics/3809842998/sizes/z/in/photostream/

       Coupled Carriages: http://www.flickr.com/photos/jowo/89657494/sizes/o/in/photostream/


Wednesday, 9 February 2011
QUESTIONS?




Wednesday, 9 February 2011
WE ARE HIRING!
                             http://www.ibuildings.co.uk/about/careers/




Wednesday, 9 February 2011

More Related Content

PPT
Testing persistence in PHP with DbUnit
PDF
JavaFX for Business Application Developers
PDF
How do we use hooks
PDF
SilverStripe CMS JavaScript Refactoring
PDF
Spring 4 - A&BP CC
ODP
Introduction to Everit Component Registry - B Zsoldos
ODP
Getting to Grips with SilverStripe Testing
PDF
Unit Testing in SilverStripe
Testing persistence in PHP with DbUnit
JavaFX for Business Application Developers
How do we use hooks
SilverStripe CMS JavaScript Refactoring
Spring 4 - A&BP CC
Introduction to Everit Component Registry - B Zsoldos
Getting to Grips with SilverStripe Testing
Unit Testing in SilverStripe

Viewers also liked (9)

PDF
Magento Imagine eCommerce Conference 2011: Using Magento's Import Module
PPTX
Magento Imagine Conference: With Friends Like These Who Needs Revenue?
PDF
Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...
PDF
Best Practices for Launching an Enterprise Business on Magento
PPSX
Magento performancenbs
ZIP
Magento Imagine eCommerce, Day 2, Yoav Kutner CTO
ZIP
Magento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEO
PDF
Driving Business Innovation with Magento
PDF
Optimizing Magento Performance with Zend Server
Magento Imagine eCommerce Conference 2011: Using Magento's Import Module
Magento Imagine Conference: With Friends Like These Who Needs Revenue?
Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...
Best Practices for Launching an Enterprise Business on Magento
Magento performancenbs
Magento Imagine eCommerce, Day 2, Yoav Kutner CTO
Magento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEO
Driving Business Innovation with Magento
Optimizing Magento Performance with Zend Server
Ad

Similar to Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento (20)

PPTX
Magento2.3 API Functional Testing
PPTX
Introduction to Integration Tests in Magento / Adobe Commerce
PPTX
Introduction to Integration Tests in Magento / Adobe Commerce
PDF
Best practices for crafting high quality PHP apps - PHP UK 2019
PPTX
Dusan Lukic Magento 2 Integration Tests Meet Magento Serbia 2016
PPT
Meet Magento DE 2016 - Kristof Ringleff - Growing up with Magento
PDF
The Solar Framework for PHP
PPTX
Backward Compatibility Developer's Guide in Magento 2. #MM17CZ
PPTX
Backward Compatibility Developer's Guide in Magento 2
PPTX
Testing in Magento 2
PDF
Test and API-driven development of CakePHP Behaviors
PDF
Doctrine in the Real World
PPTX
Growing up with Magento
PPTX
Automated tests in Magento
PDF
Unit testing PHP apps with PHPUnit
PPTX
API design best practices
PDF
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
PPTX
Backwards Compatibility Developers Guide. #MM17NL
PDF
Debugging and Magento
PPT
Building Quality with Foundations of Mud
Magento2.3 API Functional Testing
Introduction to Integration Tests in Magento / Adobe Commerce
Introduction to Integration Tests in Magento / Adobe Commerce
Best practices for crafting high quality PHP apps - PHP UK 2019
Dusan Lukic Magento 2 Integration Tests Meet Magento Serbia 2016
Meet Magento DE 2016 - Kristof Ringleff - Growing up with Magento
The Solar Framework for PHP
Backward Compatibility Developer's Guide in Magento 2. #MM17CZ
Backward Compatibility Developer's Guide in Magento 2
Testing in Magento 2
Test and API-driven development of CakePHP Behaviors
Doctrine in the Real World
Growing up with Magento
Automated tests in Magento
Unit testing PHP apps with PHPUnit
API design best practices
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
Backwards Compatibility Developers Guide. #MM17NL
Debugging and Magento
Building Quality with Foundations of Mud
Ad

More from varien (12)

PPTX
Magento's Imagine eCommerce Conference: Do You Queue?
PPTX
Magento Imagine eCommerce Conference:5 Way to Supercharge your Magento Enterp...
PPTX
Magento Imgine eCommerce Conference February 2011: Mashup of Magento and Sale...
PDF
Best Practices for Magento Debugging
PDF
Getting from Here to There: How to assess your business, define an overall eC...
PPT
Magento Roy Rubin Amsterdam Presentation
PPT
Maximizing Magento: Getting the Most out of Multi-Store Management
PPT
Maximizing Magento: Getting the Most out of Promotions
PDF
Vortrag über Magento auf der InternetWorld 2008
PPT
Selecting the 'Right' eCommerce Plaform
PPT
Magento eCommerce And The Next Generation Of PHP
PPT
Under the Radar Magento Presentation
Magento's Imagine eCommerce Conference: Do You Queue?
Magento Imagine eCommerce Conference:5 Way to Supercharge your Magento Enterp...
Magento Imgine eCommerce Conference February 2011: Mashup of Magento and Sale...
Best Practices for Magento Debugging
Getting from Here to There: How to assess your business, define an overall eC...
Magento Roy Rubin Amsterdam Presentation
Maximizing Magento: Getting the Most out of Multi-Store Management
Maximizing Magento: Getting the Most out of Promotions
Vortrag über Magento auf der InternetWorld 2008
Selecting the 'Right' eCommerce Plaform
Magento eCommerce And The Next Generation Of PHP
Under the Radar Magento Presentation

Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

  • 1. QUALITY ASSURANCE IN MAGENTO EXTENSIONS Automated Functional Testing Wednesday, 9 February 2011
  • 2. WHO AM I • Alistair Stead • Technical Team Lead @ Ibuildings UK / Session Digital • Lead Magento projects for 3663, WMI, Kookai and others • Over 3 years experience with Magento • Zend Certified Engineer • Over 11 years commercial experience developing in PHP Wednesday, 9 February 2011
  • 3. CREATING SOFTWARE IS COMPLEX Wednesday, 9 February 2011
  • 4. CREATING SOFTWARE IS COMPLEX ing it is argua bly harder) (Maintain Wednesday, 9 February 2011
  • 5. FAULT TOLERANCE IS LOW Wednesday, 9 February 2011
  • 6. “Hello, technical support?” “I would like to report a bug...” Wednesday, 9 February 2011
  • 7. E-COMMERCE HAS A LOW TOLERANCE TO FAULTS. Wednesday, 9 February 2011
  • 8. MAGENTO MAKES IT OBVIOUS SOMETHING IS WRONG Wednesday, 9 February 2011
  • 9. With whom should the BUCK stop? Wednesday, 9 February 2011
  • 10. With whom should the BUCK stop? e sure its not us.... Lets mak Wednesday, 9 February 2011
  • 11. SO WHAT CAN WE DO TO FIX THIS? Wednesday, 9 February 2011
  • 12. We are clever people right? Wednesday, 9 February 2011
  • 13. Just get more of us? Wednesday, 9 February 2011
  • 14. Testing at the eleventh hour... Wednesday, 9 February 2011
  • 15. We can use technology to help! Wednesday, 9 February 2011
  • 16. AUTOMATED TESTING FRAMEWORKS CAN DO THE HEAVY LIFTING. Selenium Watir Wednesday, 9 February 2011
  • 17. PHPUNIT The de-facto standard for unit testing in PHP projects Wednesday, 9 February 2011
  • 18. WE CAN RUN UNIT TESTS FOR MAGENTO! Wednesday, 9 February 2011
  • 19. <?php /** * Magento PHPUnit TestCase * * @package Ibuildings_Mage_Test_PHPUnit * @copyright Copyright (c) 2011 Ibuildings. (http://www.ibuildings.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @author Alistair Stead <alistair@ibuildings.com> * @version $Id$ */ /** * PHPUnit_Framework_Magento_TestCase * * @category Mage_Test * @package Ibuildings_Mage_Test_PHPUnit * @subpackage Ibuildings_Mage_Test_PHPUnit_TestCase * @uses PHPUnit_Framework_TestCase */ abstract class Ibuildings_Mage_Test_PHPUnit_TestCase extends PHPUnit_Framework_TestCase { public function setUp() { parent::setUp(); // Initialise Magento Mage::app(); } } Wednesday, 9 February 2011
  • 20. <?php /** * Magento PHPUnit TestCase * * @package Ibuildings_Mage_Test_PHPUnit * @copyright Copyright (c) 2011 Ibuildings. (http://www.ibuildings.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @author Alistair Stead <alistair@ibuildings.com> * @version $Id$ */ /** * PHPUnit_Framework_Magento_TestCase * * @category Mage_Test * @package Ibuildings_Mage_Test_PHPUnit * @subpackage Ibuildings_Mage_Test_PHPUnit_TestCase * @uses PHPUnit_Framework_TestCase */ abstract class Ibuildings_Mage_Test_PHPUnit_TestCase extends PHPUnit_Framework_TestCase { public function setUp() { parent::setUp(); // Initialise Magento Mage::app(); } } Wednesday, 9 February 2011
  • 22. <?php class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); } Wednesday, 9 February 2011
  • 23. <?php class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); } Wednesday, 9 February 2011
  • 24. <?php class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); } Wednesday, 9 February 2011
  • 25. /** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject Wednesday, 9 February 2011
  • 26. /** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject Wednesday, 9 February 2011
  • 27. /** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject Wednesday, 9 February 2011
  • 28. /** * getTransactionTypesReturnsArray * @author Alistair Stead * @test */ public function getTransactionTypesReturnsArray() { $result = $this->_object->getTansactionTypes(); $this->assertTrue(is_array($result), 'No array returned'); $this->assertEquals(count($result), 2, 'More transaction types than expected'); $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' ); $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' ); } // getTransactionTypesReturnsArray Wednesday, 9 February 2011
  • 29. /** * getTransactionTypesReturnsArray * @author Alistair Stead * @test */ public function getTransactionTypesReturnsArray() { $result = $this->_object->getTansactionTypes(); $this->assertTrue(is_array($result), 'No array returned'); $this->assertEquals(count($result), 2, 'More transaction types than expected'); $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' ); $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' ); } // getTransactionTypesReturnsArray Wednesday, 9 February 2011
  • 31. /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_helper = Mage::helper('contactsdepartment'); $this->_departmentIds = array(); $department = Mage::getModel('contactsdepartment/department'); $fixtures = array( array( 'department_name' => 'department one', 'department_email' => 'email1@department.com', 'store_id' => 0 ) ); foreach ($fixtures as $data) { $department->addData($data); $department->save(); $this->_departmentIds[] = $department->getId(); } } Wednesday, 9 February 2011
  • 32. /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_helper = Mage::helper('contactsdepartment'); $this->_departmentIds = array(); $department = Mage::getModel('contactsdepartment/department'); $fixtures = array( array( 'department_name' => 'department one', 'department_email' => 'email1@department.com', 'store_id' => 0 ) ); foreach ($fixtures as $data) { $department->addData($data); $department->save(); $this->_departmentIds[] = $department->getId(); } } Wednesday, 9 February 2011
  • 33. /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_helper = Mage::helper('contactsdepartment'); $this->_departmentIds = array(); $department = Mage::getModel('contactsdepartment/department'); $fixtures = array( array( 'department_name' => 'department one', 'department_email' => 'email1@department.com', 'store_id' => 0 ) ); foreach ($fixtures as $data) { $department->addData($data); $department->save(); $this->_departmentIds[] = $department->getId(); } } Wednesday, 9 February 2011
  • 34. /** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray Wednesday, 9 February 2011
  • 35. /** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray Wednesday, 9 February 2011
  • 36. /** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray Wednesday, 9 February 2011
  • 37. Okay we’re done lets go home... Wednesday, 9 February 2011
  • 38. Hold on, we have not really done anything new. Wednesday, 9 February 2011
  • 39. DID WE MENTION FUNCTIONAL TESTING YET? Wednesday, 9 February 2011
  • 40. QUALITY ASSURANCE IN MAGENTO EXTENSIONS Automated Functional Testing Wednesday, 9 February 2011
  • 42. MAGENTO IS CLOSELY COUPLED TO MAGE_CORE_APPLICATION Wednesday, 9 February 2011
  • 43. Coupled components cause inflexibility Wednesday, 9 February 2011
  • 44. Dependency injection is the key Wednesday, 9 February 2011
  • 45. MAGE-TEST http://github.com/ibuildings/Mage-Test Wednesday, 9 February 2011
  • 46. Mage_Core_Model_App Ibuildings_Mage_Controller_Request_HttpTestCase Ibuildings_Mage_Controller_Response_HttpTestCase Ibuildings_Mage_Test_PHPUnit_ControllerTestCase Ibuildings_Mage_Test_PHPUnit_TestCase Wednesday, 9 February 2011
  • 47. MAGE_CORE_APPLICATION Loaded from the community code pool Wednesday, 9 February 2011
  • 48. /** * Provide a public method to allow the internal Request object * to be set at runtime. This can be used to inject a testing request object * * @return void * @author Alistair Stead **/ public function setRequest(Zend_Controller_Request_Abstract $request) { $this->_request = $request; } /** * Retrieve request object * * @return Mage_Core_Controller_Request_Http */ public function getRequest() { if (empty($this->_request)) { $this->_request = new Mage_Core_Controller_Request_Http(); } return $this->_request; } Wednesday, 9 February 2011
  • 49. /** * Provide a public method to allow the internal Request object * to be set at runtime. This can be used to inject a testing request object * * @return void * @author Alistair Stead **/ public function setRequest(Zend_Controller_Request_Abstract $request) { $this->_request = $request; } /** * Retrieve request object * * @return Mage_Core_Controller_Request_Http */ public function getRequest() { if (empty($this->_request)) { $this->_request = new Mage_Core_Controller_Request_Http(); } return $this->_request; } Wednesday, 9 February 2011
  • 50. /** * Provide a public method to allow the protected internal Response object * to be set at runtime. This can be used to inject a testing response object * * @return void * @author Alistair Stead **/ public function setResponse(Zend_Controller_Response_Abstract $response) { $this->_response = $response; } /** * Retrieve response object * * @return Zend_Controller_Response_Http */ public function getResponse() { if (empty($this->_response)) { $this->_response = new Mage_Core_Controller_Response_Http(); $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException; $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8"); } return $this->_response; } Wednesday, 9 February 2011
  • 51. /** * Provide a public method to allow the protected internal Response object * to be set at runtime. This can be used to inject a testing response object * * @return void * @author Alistair Stead **/ public function setResponse(Zend_Controller_Response_Abstract $response) { $this->_response = $response; } /** * Retrieve response object * * @return Zend_Controller_Response_Http */ public function getResponse() { if (empty($this->_response)) { $this->_response = new Mage_Core_Controller_Response_Http(); $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException; $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8"); } return $this->_response; } Wednesday, 9 February 2011
  • 52. CONTROLLER TESTS Ibuildings_Mage_Test_PHPUnit_ControllerTestCase Wednesday, 9 February 2011
  • 53. /** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; // Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); } Wednesday, 9 February 2011
  • 54. /** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; // Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); } Wednesday, 9 February 2011
  • 55. /** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; // Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); } Wednesday, 9 February 2011
  • 56. Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCase Wednesday, 9 February 2011
  • 57. Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCase Wednesday, 9 February 2011
  • 58. Mage_Core_Model_App Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCase Wednesday, 9 February 2011
  • 59. <?php /** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */ class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase { /** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot } Wednesday, 9 February 2011
  • 60. <?php /** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */ class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase { /** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot } Wednesday, 9 February 2011
  • 61. <?php /** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */ class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase { /** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot } Wednesday, 9 February 2011
  • 62. /** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm Wednesday, 9 February 2011
  • 63. /** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm Wednesday, 9 February 2011
  • 64. /** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm Wednesday, 9 February 2011
  • 65. /** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm Wednesday, 9 February 2011
  • 66. /** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm Wednesday, 9 February 2011
  • 67. /** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) ); $this->dispatch('admin/index/login'); $this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError Wednesday, 9 February 2011
  • 68. /** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) ); $this->dispatch('admin/index/login'); $this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError Wednesday, 9 February 2011
  • 69. /** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) ); $this->dispatch('admin/index/login'); $this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError Wednesday, 9 February 2011
  • 70. /** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) ); $this->dispatch('admin/index/login'); $this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError Wednesday, 9 February 2011
  • 71. /** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) ); $this->dispatch('admin/index/login'); $this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError Wednesday, 9 February 2011
  • 72. /** * submittingValidCredsShouldDisplayDashboard * @author Alistair Stead * @group login * @test */ public function submittingValidCredsShouldDisplayDashboard() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); $this->assertRedirect('We should be redirected after login'); $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard'); } // submittingValidCredsShouldDisplayDashboard Wednesday, 9 February 2011
  • 73. /** * submittingValidCredsShouldDisplayDashboard * @author Alistair Stead * @group login * @test */ public function submittingValidCredsShouldDisplayDashboard() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); $this->assertRedirect('We should be redirected after login'); $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard'); } // submittingValidCredsShouldDisplayDashboard Wednesday, 9 February 2011
  • 74. class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase { /** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset(); $this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders } Wednesday, 9 February 2011
  • 75. class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase { /** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset(); $this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders } Wednesday, 9 February 2011
  • 76. class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase { /** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset(); $this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders } Wednesday, 9 February 2011
  • 77. TESTING EMAIL CONTENT Who gets all the emails sent to test@example.com? Wednesday, 9 February 2011
  • 78. public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; // Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); // Rewrite the core classes at runtime to prevent emails from being sent Mage::getConfig()->setNode('global/models/core/rewrite/email_template', 'Ibuildings_Test_Model_Email_Template'); // This is a hack to get the runtime config changes to take effect Mage::getModel('core/email_template'); } Wednesday, 9 February 2011
  • 79. public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : ''; // Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); // Rewrite the core classes at runtime to prevent emails from being sent Mage::getConfig()->setNode('global/models/core/rewrite/email_template', 'Ibuildings_Test_Model_Email_Template'); // This is a hack to get the runtime config changes to take effect Mage::getModel('core/email_template'); } Wednesday, 9 February 2011
  • 80. /** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess Wednesday, 9 February 2011
  • 81. /** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess Wednesday, 9 February 2011
  • 82. /** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess Wednesday, 9 February 2011
  • 83. /** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess Wednesday, 9 February 2011
  • 84. SO WHAT DO AUTOMATED TESTS GIVE US? Wednesday, 9 February 2011
  • 85. CODE COVERAGE Wednesday, 9 February 2011
  • 90. SO WHAT NOW? Wednesday, 9 February 2011
  • 91. MAGE-TEST IS OPEN SOURCE • Use Mage-Test to test your development projects • Use Mage-Test to test you extensions • Contribute tests for Magento • Build up the coverage of the Magento codebase • Take advantage of core tests for regression testing Wednesday, 9 February 2011
  • 92. MAGE-TEST http://github.com/ibuildings/Mage-Test Wednesday, 9 February 2011
  • 93. THANK YOU! • Email: astead@ibuildings.com • Skype: astead-ibuildings • Twitter: @alistairstead Wednesday, 9 February 2011
  • 94. REFERENCES • PHPUnit https://github.com/sebastianbergmann/phpunit/ • Zend_Test http://framework.zend.com/manual/en/ zend.test.html • Mage-Test https://github.com/ibuildings/Mage-Test • phpUnderControl http://phpundercontrol.org/ • Bamboo http://www.atlassian.com/software/bamboo/ Wednesday, 9 February 2011
  • 95. IMAGE CREDITS BA Plane: http://www.flickr.com/photos/bribri/1299325208/sizes/l/in/photostream/ Minions: http://www.akblessingsabound.com/wp-content/uploads/2010/06/despicable-me-minions- blessings-abound-mommy.jpg Eleventh Hour: http://www.flickr.com/photos/d4dee/2258343575/sizes/l/in/photostream/ Deep Thought: http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/ US Dollar: http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/ Whoa (Stop Sign): http://www.flickr.com/photos/aquaoracle/3265987824/sizes/l/ Departures Board: http://www.flickr.com/photos/scottmulhollan/4892422469/sizes/l/in/photostream/ Syringe: http://www.flickr.com/photos/woodypics/3809842998/sizes/z/in/photostream/ Coupled Carriages: http://www.flickr.com/photos/jowo/89657494/sizes/o/in/photostream/ Wednesday, 9 February 2011
  • 97. WE ARE HIRING! http://www.ibuildings.co.uk/about/careers/ Wednesday, 9 February 2011