• Like
  • Save
Magento's Imagine eCommerce Conference 2011 - Unit Testing with Magento
Upcoming SlideShare
Loading in...5
×
 

Magento's Imagine eCommerce Conference 2011 - Unit Testing with Magento

on

  • 1,128 views

 

Statistics

Views

Total Views
1,128
Views on SlideShare
1,024
Embed Views
104

Actions

Likes
3
Downloads
15
Comments
0

3 Embeds 104

http://www.imagineecommerce.com 63
http://beta.imagineecommerce.com 40
http://origin-www.imagineecommerce.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's Imagine eCommerce Conference 2011 - Unit Testing with Magento Magento's Imagine eCommerce Conference 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