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

Like this? Share it with your network

Share

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

on

  • 1,243 views

 

Statistics

Views

Total Views
1,243
Views on SlideShare
1,135
Embed Views
108

Actions

Likes
3
Downloads
17
Comments
0

3 Embeds 108

http://www.imagineecommerce.com 67
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 Presentation Transcript

  • 1. QUALITY ASSURANCE IN MAGENTO EXTENSIONS Automated Functional TestingWednesday, 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 PHPWednesday, 9 February 2011
  • 3. CREATING SOFTWARE IS COMPLEXWednesday, 9 February 2011
  • 4. CREATING SOFTWARE IS COMPLEX ing it is argua bly harder) (MaintainWednesday, 9 February 2011
  • 5. FAULT TOLERANCE IS LOWWednesday, 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 WRONGWednesday, 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 makWednesday, 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 WatirWednesday, 9 February 2011
  • 17. PHPUNIT The de-facto standard for unit testing in PHP projectsWednesday, 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
  • 21. MODELSWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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); } // mageSingletonFactoryReturnsTheSameObjectWednesday, 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 ); } // getTransactionTypesReturnsArrayWednesday, 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 ); } // getTransactionTypesReturnsArrayWednesday, 9 February 2011
  • 30. HELPERSWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 ); } } // getDepartmentOptionsShouldReturnAssociativeArrayWednesday, 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 TestingWednesday, 9 February 2011
  • 41. ZEND_TEST?Wednesday, 9 February 2011
  • 42. MAGENTO IS CLOSELY COUPLED TO MAGE_CORE_APPLICATIONWednesday, 9 February 2011
  • 43. Coupled components cause inflexibilityWednesday, 9 February 2011
  • 44. Dependency injection is the keyWednesday, 9 February 2011
  • 45. MAGE-TEST http://github.com/ibuildings/Mage-TestWednesday, 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_TestCaseWednesday, 9 February 2011
  • 47. MAGE_CORE_APPLICATION Loaded from the community code poolWednesday, 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_ControllerTestCaseWednesday, 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_HttpTestCaseWednesday, 9 February 2011
  • 57. Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCaseWednesday, 9 February 2011
  • 58. Mage_Core_Model_App Ibuildings_Mage_Controller_Request_HttpTestCase Model View Controller Ibuildings_Mage_Controller_Response_HttpTestCaseWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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); } // theIndexActionDisplaysLoginFormWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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.); } // submittingInvalidCredsShouldDisplayErrorWednesday, 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); } // submittingValidCredsShouldDisplayDashboardWednesday, 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); } // submittingValidCredsShouldDisplayDashboardWednesday, 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 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
  • 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 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
  • 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 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
  • 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 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
  • 84. SO WHAT DO AUTOMATED TESTS GIVE US?Wednesday, 9 February 2011
  • 85. CODE COVERAGEWednesday, 9 February 2011
  • 86. STATIC CODE ANALYSISWednesday, 9 February 2011
  • 87. CONTINUOUS INTEGRATIONWednesday, 9 February 2011
  • 88. Wednesday, 9 February 2011
  • 89. 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 testingWednesday, 9 February 2011
  • 92. MAGE-TEST http://github.com/ibuildings/Mage-TestWednesday, 9 February 2011
  • 93. THANK YOU! • Email: astead@ibuildings.com • Skype: astead-ibuildings • Twitter: @alistairsteadWednesday, 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
  • 96. QUESTIONS?Wednesday, 9 February 2011
  • 97. WE ARE HIRING! http://www.ibuildings.co.uk/about/careers/Wednesday, 9 February 2011