SlideShare une entreprise Scribd logo
1  sur  97
Télécharger pour lire hors ligne
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

Contenu connexe

En vedette

Magento Imagine eCommerce Conference 2011: Using Magento's Import Module
Magento Imagine eCommerce Conference 2011: Using Magento's Import ModuleMagento Imagine eCommerce Conference 2011: Using Magento's Import Module
Magento Imagine eCommerce Conference 2011: Using Magento's Import Modulevarien
 
Magento Imagine Conference: With Friends Like These Who Needs Revenue?
Magento Imagine Conference: With Friends Like These Who Needs Revenue?Magento Imagine Conference: With Friends Like These Who Needs Revenue?
Magento Imagine Conference: With Friends Like These Who Needs Revenue?varien
 
Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...
Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...
Magento Imagine eCommerce Conference February 2011: Optimizing Magento For Pe...varien
 
Best Practices for Launching an Enterprise Business on Magento
Best Practices for Launching an Enterprise Business on MagentoBest Practices for Launching an Enterprise Business on Magento
Best Practices for Launching an Enterprise Business on Magentovarien
 
Magento performancenbs
Magento performancenbsMagento performancenbs
Magento performancenbsvarien
 
Magento Imagine eCommerce, Day 2, Yoav Kutner CTO
Magento Imagine eCommerce, Day 2, Yoav Kutner CTOMagento Imagine eCommerce, Day 2, Yoav Kutner CTO
Magento Imagine eCommerce, Day 2, Yoav Kutner CTOvarien
 
Magento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEO
Magento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEOMagento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEO
Magento Imagine eCommerce, Day 1, Roy Rubin Co-Founder & CEOvarien
 
Driving Business Innovation with Magento
Driving Business Innovation with MagentoDriving Business Innovation with Magento
Driving Business Innovation with Magentovarien
 
Optimizing Magento Performance with Zend Server
Optimizing Magento Performance with Zend ServerOptimizing Magento Performance with Zend Server
Optimizing Magento Performance with Zend Servervarien
 

En vedette (9)

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

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

Node conf - building realtime webapps
Node conf - building realtime webappsNode conf - building realtime webapps
Node conf - building realtime webappsHenrik Joreteg
 
What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...Richard McIntyre
 
The Solar Framework for PHP
The Solar Framework for PHPThe Solar Framework for PHP
The Solar Framework for PHPConFoo
 
Magento Attributes - Fresh View
Magento Attributes - Fresh ViewMagento Attributes - Fresh View
Magento Attributes - Fresh ViewAlex Gotgelf
 
Unit testing: unitils & dbmaintain
Unit testing: unitils & dbmaintainUnit testing: unitils & dbmaintain
Unit testing: unitils & dbmaintainnevenfi
 
Writing jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryWriting jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryRoss Bruniges
 
international PHP2011_ilia alshanetsky_Hidden Features of PHP
international PHP2011_ilia alshanetsky_Hidden Features of PHPinternational PHP2011_ilia alshanetsky_Hidden Features of PHP
international PHP2011_ilia alshanetsky_Hidden Features of PHPsmueller_sandsmedia
 
Mike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to youMike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to youStarTech Conference
 
잘 알려지지 않은 Php 코드 활용하기
잘 알려지지 않은 Php 코드 활용하기잘 알려지지 않은 Php 코드 활용하기
잘 알려지지 않은 Php 코드 활용하기형우 안
 
Frozen Rails Slides
Frozen Rails SlidesFrozen Rails Slides
Frozen Rails Slidescarllerche
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to javaciklum_ods
 
Nashville Symfony Functional Testing
Nashville Symfony Functional TestingNashville Symfony Functional Testing
Nashville Symfony Functional TestingBrent Shaffer
 
jQuery Internals + Cool Stuff
jQuery Internals + Cool StuffjQuery Internals + Cool Stuff
jQuery Internals + Cool Stuffjeresig
 
Tools for Development and Debugging in Python
Tools for Development and Debugging in PythonTools for Development and Debugging in Python
Tools for Development and Debugging in Pythonzeeg
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadDavid Gómez García
 
Turbo Charged Test Suites
Turbo Charged Test SuitesTurbo Charged Test Suites
Turbo Charged Test SuitesCurtis Poe
 
How to write better code: in-depth best practices for writing readable, simpl...
How to write better code: in-depth best practices for writing readable, simpl...How to write better code: in-depth best practices for writing readable, simpl...
How to write better code: in-depth best practices for writing readable, simpl...Oursky
 

Similaire à Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento (20)

Node conf - building realtime webapps
Node conf - building realtime webappsNode conf - building realtime webapps
Node conf - building realtime webapps
 
What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...
 
The Solar Framework for PHP
The Solar Framework for PHPThe Solar Framework for PHP
The Solar Framework for PHP
 
Magento Attributes - Fresh View
Magento Attributes - Fresh ViewMagento Attributes - Fresh View
Magento Attributes - Fresh View
 
Unit testing: unitils & dbmaintain
Unit testing: unitils & dbmaintainUnit testing: unitils & dbmaintain
Unit testing: unitils & dbmaintain
 
Writing jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryWriting jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQuery
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
international PHP2011_ilia alshanetsky_Hidden Features of PHP
international PHP2011_ilia alshanetsky_Hidden Features of PHPinternational PHP2011_ilia alshanetsky_Hidden Features of PHP
international PHP2011_ilia alshanetsky_Hidden Features of PHP
 
Mike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to youMike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to you
 
잘 알려지지 않은 Php 코드 활용하기
잘 알려지지 않은 Php 코드 활용하기잘 알려지지 않은 Php 코드 활용하기
잘 알려지지 않은 Php 코드 활용하기
 
Frozen Rails Slides
Frozen Rails SlidesFrozen Rails Slides
Frozen Rails Slides
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Nashville Symfony Functional Testing
Nashville Symfony Functional TestingNashville Symfony Functional Testing
Nashville Symfony Functional Testing
 
Testable Javascript
Testable JavascriptTestable Javascript
Testable Javascript
 
jQuery Internals + Cool Stuff
jQuery Internals + Cool StuffjQuery Internals + Cool Stuff
jQuery Internals + Cool Stuff
 
Tools for Development and Debugging in Python
Tools for Development and Debugging in PythonTools for Development and Debugging in Python
Tools for Development and Debugging in Python
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidad
 
Turbo Charged Test Suites
Turbo Charged Test SuitesTurbo Charged Test Suites
Turbo Charged Test Suites
 
Ruby on Rails 101
Ruby on Rails 101Ruby on Rails 101
Ruby on Rails 101
 
How to write better code: in-depth best practices for writing readable, simpl...
How to write better code: in-depth best practices for writing readable, simpl...How to write better code: in-depth best practices for writing readable, simpl...
How to write better code: in-depth best practices for writing readable, simpl...
 

Plus de varien

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

Plus de varien (12)

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