Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 2,338 views

Unit Testing with Magento

Unit Testing with Magento
Alistair Stead, Technical Team Lead,
Session Digital, an Ibuildings Company

Statistics

Views

Total Views
2,338
Views on SlideShare
2,300
Embed Views
38

Actions

Likes
4
Downloads
36
Comments
0

3 Embeds 38

http://www.magentocommerce.com 36
http://www.techgig.com 1
http://www.m.techgig.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • QUALITY ASSURANCE IN MAGENTO EXTENSIONS Automated Functional TestingWednesday, 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 PHPWednesday, 9 February 2011
  • CREATING SOFTWARE IS COMPLEXWednesday, 9 February 2011
  • CREATING SOFTWARE IS COMPLEX ing it is argua bly harder) (MaintainWednesday, 9 February 2011
  • FAULT TOLERANCE IS LOWWednesday, 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 WRONGWednesday, 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 makWednesday, 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 WatirWednesday, 9 February 2011
  • PHPUNIT The de-facto standard for unit testing in PHP projectsWednesday, 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
  • MODELSWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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 ); } // getTransactionTypesReturnsArrayWednesday, 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 ); } // getTransactionTypesReturnsArrayWednesday, 9 February 2011
  • HELPERSWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 TestingWednesday, 9 February 2011
  • ZEND_TEST?Wednesday, 9 February 2011
  • MAGENTO IS CLOSELY COUPLED TO MAGE_CORE_APPLICATIONWednesday, 9 February 2011
  • Coupled components cause inflexibilityWednesday, 9 February 2011
  • Dependency injection is the keyWednesday, 9 February 2011
  • MAGE-TEST http://github.com/ibuildings/Mage-TestWednesday, 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_TestCaseWednesday, 9 February 2011
  • MAGE_CORE_APPLICATION Loaded from the community code poolWednesday, 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_ControllerTestCaseWednesday, 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_HttpTestCaseWednesday, 9 February 2011
  • Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCaseWednesday, 9 February 2011
  • Mage_Core_Model_App Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCaseWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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); } // submittingValidCredsShouldDisplayDashboardWednesday, 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); } // submittingValidCredsShouldDisplayDashboardWednesday, 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 emailaddress. 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); } // submittingForgotPasswordWithValidEmailReturnsSuccessWednesday, 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 emailaddress. 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); } // submittingForgotPasswordWithValidEmailReturnsSuccessWednesday, 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 emailaddress. 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); } // submittingForgotPasswordWithValidEmailReturnsSuccessWednesday, 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 emailaddress. 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); } // submittingForgotPasswordWithValidEmailReturnsSuccessWednesday, 9 February 2011
  • SO WHAT DO AUTOMATED TESTS GIVE US?Wednesday, 9 February 2011
  • CODE COVERAGEWednesday, 9 February 2011
  • STATIC CODE ANALYSISWednesday, 9 February 2011
  • CONTINUOUS INTEGRATIONWednesday, 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 testingWednesday, 9 February 2011
  • MAGE-TEST http://github.com/ibuildings/Mage-TestWednesday, 9 February 2011
  • THANK YOU! • Email: astead@ibuildings.com • Skype: astead-ibuildings • Twitter: @alistairsteadWednesday, 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