SlideShare uma empresa Scribd logo
1 de 97
Baixar para ler offline
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

Mais conteúdo relacionado

Semelhante a Magento's Imagine eCommerce Conference 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
 

Semelhante a Magento's Imagine eCommerce Conference 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...
 

Mais de MagentoImagine

Driving Business Innovation with Magento
Driving Business Innovation with MagentoDriving Business Innovation with Magento
Driving Business Innovation with MagentoMagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 Keynote
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 KeynoteMagento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 Keynote
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 KeynoteMagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 Keynote
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 KeynoteMagento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 Keynote
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 KeynoteMagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...MagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...MagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...MagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - eCommerce Strategy
Magento's Imagine eCommerce Conference 2011 - eCommerce StrategyMagento's Imagine eCommerce Conference 2011 - eCommerce Strategy
Magento's Imagine eCommerce Conference 2011 - eCommerce StrategyMagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...MagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...MagentoImagine
 
Magento's Imagine eCommerce Conference 2011 - Do You Queue?
Magento's Imagine eCommerce Conference 2011 - Do You Queue?Magento's Imagine eCommerce Conference 2011 - Do You Queue?
Magento's Imagine eCommerce Conference 2011 - Do You Queue?MagentoImagine
 

Mais de MagentoImagine (10)

Driving Business Innovation with Magento
Driving Business Innovation with MagentoDriving Business Innovation with Magento
Driving Business Innovation with Magento
 
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 Keynote
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 KeynoteMagento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 Keynote
Magento's Imagine eCommerce Conference 2011 - Yoav Kutner Day 2 Keynote
 
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 Keynote
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 KeynoteMagento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 Keynote
Magento's Imagine eCommerce Conference 2011 - Roy Rubin Day 1 Keynote
 
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...
Magento's Imagine eCommerce Conference 2011 - Mash-up of Magento and Salesfor...
 
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...
Magento's Imagine eCommerce Conference 2011 - Delivering Conversion Results b...
 
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...
Magento's Imagine eCommerce Conference 2011 - Hosting Magento: Performance an...
 
Magento's Imagine eCommerce Conference 2011 - eCommerce Strategy
Magento's Imagine eCommerce Conference 2011 - eCommerce StrategyMagento's Imagine eCommerce Conference 2011 - eCommerce Strategy
Magento's Imagine eCommerce Conference 2011 - eCommerce Strategy
 
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...
Magento's Imagine eCommerce Conference 2011 - Import Export in a Flash with t...
 
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...
Magento's Imagine eCommerce Conference 2011 - With Friends Like These, Who Ne...
 
Magento's Imagine eCommerce Conference 2011 - Do You Queue?
Magento's Imagine eCommerce Conference 2011 - Do You Queue?Magento's Imagine eCommerce Conference 2011 - Do You Queue?
Magento's Imagine eCommerce Conference 2011 - Do You Queue?
 

Último

The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 

Último (20)

The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 

Magento's Imagine eCommerce Conference 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