QUALITY ASSURANCE IN MAGENTO EXTENSIONS
Automated Functional Testing
Wednesday, 9 February 2011
WHO AM I• Alistair Stead
• Technical Team Lead @ Ibuildings UK / Session Digital
• Lead Magento projects for 3663, WMI, Kookai and others
•Over 3 years experience with Magento
• Zend Certified Engineer
•Over 11 years commercial experience developing in PHP
Wednesday, 9 February 2011
CREATING SOFTWARE IS COMPLEX
Wednesday, 9 February 2011
CREATING SOFTWARE IS COMPLEX
(Maintaining it is arguably harder)
Wednesday, 9 February 2011
FAULT TOLERANCE IS
LOW
Wednesday, 9 February 2011
“Hello, technical support?”
“I would like to report a bug...”
Wednesday, 9 February 2011
E-COMMERCE HAS A LOW TOLERANCE TO FAULTS.
Wednesday, 9 February 2011
MAGENTO MAKES IT OBVIOUS SOMETHING IS
WRONG
Wednesday, 9 February 2011
With whom should the BUCK stop?
Wednesday, 9 February 2011
With whom should the BUCK stop?
Lets make sure its not us....
Wednesday, 9 February 2011
SO WHAT CAN WE DO TO FIX THIS?
Wednesday, 9 February 2011
We are clever people right?
Wednesday, 9 February 2011
Just get more of us?
Wednesday, 9 February 2011
Testing at the eleventh hour...
Wednesday, 9 February 2011
We can use technology
to help!
Wednesday, 9 February 2011
AUTOMATED TESTING FRAMEWORKS CAN DO THE
HEAVY LIFTING.
Selenium Watir
Wednesday, 9 February 2011
PHPUNITThe de-facto standard for unit testing in PHP projects
Wednesday, 9 February 2011
WE CAN RUN UNIT TESTS FOR MAGENTO!
Wednesday, 9 February 2011
<?php/** * Magento PHPUnit TestCase * * @package Ibuildings_Mage_Test_PHPUnit * @copyright Copyright (c) 2011 Ibuildings. (http://www.ibuildings.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @author Alistair Stead <[email protected]> * @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 <[email protected]> * @version $Id$ */
/** * PHPUnit_Framework_Magento_TestCase * * @category Mage_Test * @package Ibuildings_Mage_Test_PHPUnit * @subpackage Ibuildings_Mage_Test_PHPUnit_TestCase * @uses PHPUnit_Framework_TestCase */abstract class Ibuildings_Mage_Test_PHPUnit_TestCase extends PHPUnit_Framework_TestCase {
public function setUp() { parent::setUp(); // Initialise Magento Mage::app(); }}
Wednesday, 9 February 2011
MODELS
Wednesday, 9 February 2011
<?php
class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); }
Wednesday, 9 February 2011
<?php
class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); }
Wednesday, 9 February 2011
<?php
class DataCash_Dpg_Model_ConfigTest extends Ibuildings_Mage_Test_PHPUnit_TestCase { /** * Member variable to hold reference to the opbject under test * * @var DataCash_Dpg_Model_Config **/ protected $_object; /** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_object = Mage::getSingleton('dpg/config'); }
Wednesday, 9 February 2011
/** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject
Wednesday, 9 February 2011
/** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject
Wednesday, 9 February 2011
/** * mageSingletonFactoryReturnsExpectedObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsExpectedObject() { $this->assertInstanceOf('DataCash_Dpg_Model_Config', $this->_object, 'Unexpected object returned by factory'); } // mageSingletonFactoryReturnsExpectedObject /** * mageSingletonFactoryReturnsTheSameObject * @author Alistair Stead * @test */ public function mageSingletonFactoryReturnsTheSameObject() { $this->assertSame($this->_object, Mage::getSingleton('dpg/config'), 'Two different objects have been created'); } // mageSingletonFactoryReturnsTheSameObject
Wednesday, 9 February 2011
/** * getTransactionTypesReturnsArray * @author Alistair Stead * @test */ public function getTransactionTypesReturnsArray() { $result = $this->_object->getTansactionTypes(); $this->assertTrue(is_array($result), 'No array returned'); $this->assertEquals(count($result), 2, 'More transaction types than expected'); $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' ); $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' ); } // getTransactionTypesReturnsArray
Wednesday, 9 February 2011
/** * getTransactionTypesReturnsArray * @author Alistair Stead * @test */ public function getTransactionTypesReturnsArray() { $result = $this->_object->getTansactionTypes(); $this->assertTrue(is_array($result), 'No array returned'); $this->assertEquals(count($result), 2, 'More transaction types than expected'); $this->assertArrayHasKey( 'A', $result, 'The array does not contain A' ); $this->assertArrayHasKey( 'P', $result, 'The array does not contain P' ); } // getTransactionTypesReturnsArray
Wednesday, 9 February 2011
HELPERS
Wednesday, 9 February 2011
/** * Setup fixtures and dependencies * * @return void * @author Alistair Stead **/ public function setUp() { parent::setUp(); $this->_helper = Mage::helper('contactsdepartment'); $this->_departmentIds = array(); $department = Mage::getModel('contactsdepartment/department'); $fixtures = array( array( 'department_name' => 'department one', 'department_email' => '[email protected]', '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' => '[email protected]', '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' => '[email protected]', 'store_id' => 0 ) ); foreach ($fixtures as $data) { $department->addData($data); $department->save(); $this->_departmentIds[] = $department->getId(); } }
Wednesday, 9 February 2011
/** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray
Wednesday, 9 February 2011
/** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray
Wednesday, 9 February 2011
/** * helperFactoryReturnsTheExpectedClass * @author Alistair Stead * @test */ public function helperFactoryReturnsTheExpectedClass() { $this->assertInstanceOf('Ibuildings_ContactsDepartment_Helper_Data', $this->_helper); } // helperFactoryReturnsTheExpectedClass /** * getDepartmentOptionsShouldReturnAssociativeArray * @author Alistair Stead * @test */ public function getDepartmentOptionsShouldReturnAssociativeArray() { foreach ($this->_helper->getDepartmentOptions() as $option) { $this->assertArrayHasKey( 'value', $option, 'The options array does not have a value' ); $this->assertArrayHasKey( 'label', $option, 'The options array does not have a label' ); } } // getDepartmentOptionsShouldReturnAssociativeArray
Wednesday, 9 February 2011
Okay we’re done lets go home...
Wednesday, 9 February 2011
Hold on, we have not really done anything new.
Wednesday, 9 February 2011
DID WE MENTION FUNCTIONAL TESTING YET?
Wednesday, 9 February 2011
QUALITY ASSURANCE IN MAGENTO EXTENSIONS
Automated Functional Testing
Wednesday, 9 February 2011
ZEND_TEST?
Wednesday, 9 February 2011
MAGENTO IS CLOSELY COUPLED TO
MAGE_CORE_APPLICATION
Wednesday, 9 February 2011
Coupled components cause inflexibilityWednesday, 9 February 2011
Dependency injection is the key
Wednesday, 9 February 2011
MAGE-TESThttp://github.com/ibuildings/Mage-Test
Wednesday, 9 February 2011
Mage_Core_Model_App
Ibuildings_Mage_Controller_Request_HttpTestCase
Ibuildings_Mage_Controller_Response_HttpTestCase
Ibuildings_Mage_Test_PHPUnit_ControllerTestCase
Ibuildings_Mage_Test_PHPUnit_TestCase
Wednesday, 9 February 2011
MAGE_CORE_APPLICATIONLoaded from the community code pool
Wednesday, 9 February 2011
/** * Provide a public method to allow the internal Request object * to be set at runtime. This can be used to inject a testing request object * * @return void * @author Alistair Stead **/ public function setRequest(Zend_Controller_Request_Abstract $request) { $this->_request = $request; }
/** * Retrieve request object * * @return Mage_Core_Controller_Request_Http */ public function getRequest() { if (empty($this->_request)) { $this->_request = new Mage_Core_Controller_Request_Http(); } return $this->_request; }
Wednesday, 9 February 2011
/** * Provide a public method to allow the internal Request object * to be set at runtime. This can be used to inject a testing request object * * @return void * @author Alistair Stead **/ public function setRequest(Zend_Controller_Request_Abstract $request) { $this->_request = $request; }
/** * Retrieve request object * * @return Mage_Core_Controller_Request_Http */ public function getRequest() { if (empty($this->_request)) { $this->_request = new Mage_Core_Controller_Request_Http(); } return $this->_request; }
Wednesday, 9 February 2011
/** * Provide a public method to allow the protected internal Response object * to be set at runtime. This can be used to inject a testing response object * * @return void * @author Alistair Stead **/ public function setResponse(Zend_Controller_Response_Abstract $response) { $this->_response = $response; }
/** * Retrieve response object * * @return Zend_Controller_Response_Http */ public function getResponse() { if (empty($this->_response)) { $this->_response = new Mage_Core_Controller_Response_Http(); $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException; $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8"); } return $this->_response; }
Wednesday, 9 February 2011
/** * Provide a public method to allow the protected internal Response object * to be set at runtime. This can be used to inject a testing response object * * @return void * @author Alistair Stead **/ public function setResponse(Zend_Controller_Response_Abstract $response) { $this->_response = $response; }
/** * Retrieve response object * * @return Zend_Controller_Response_Http */ public function getResponse() { if (empty($this->_response)) { $this->_response = new Mage_Core_Controller_Response_Http(); $this->_response->headersSentThrowsException = Mage::$headersSentThrowsException; $this->_response->setHeader("Content-Type", "text/html; charset=UTF-8"); } return $this->_response; }
Wednesday, 9 February 2011
CONTROLLER TESTSIbuildings_Mage_Test_PHPUnit_ControllerTestCase
Wednesday, 9 February 2011
/** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';
// Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); }
Wednesday, 9 February 2011
/** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';
// Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); }
Wednesday, 9 February 2011
/** * Bootstrap the Mage application in a similar way to the procedure * of index.php * * Then sets test case request and response objects in Mage_Core_App, * and disables returning the response. * * @return void * @author Alistair Stead */ public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';
// Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); }
Wednesday, 9 February 2011
Ibuildings_Mage_Controller_Response_HttpTestCase
Controller
Ibuildings_Mage_Controller_Request_HttpTestCase
View
Model
Ibuildings_Mage_Controller_Response_HttpTestCase
Wednesday, 9 February 2011
Ibuildings_Mage_Controller_Response_HttpTestCase
Controller
Ibuildings_Mage_Controller_Request_HttpTestCase
View
Model
Ibuildings_Mage_Controller_Request_HttpTestCase
Ibuildings_Mage_Controller_Response_HttpTestCase
Wednesday, 9 February 2011
Mage_Core_Model_App
Ibuildings_Mage_Controller_Response_HttpTestCase
Controller
Ibuildings_Mage_Controller_Request_HttpTestCase
View
Model
Ibuildings_Mage_Controller_Request_HttpTestCase
Ibuildings_Mage_Controller_Response_HttpTestCase
Wednesday, 9 February 2011
<?php
/** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {
/** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot}
Wednesday, 9 February 2011
<?php
/** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {
/** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot}
Wednesday, 9 February 2011
<?php
/** * Mage_Catalog_IndexControllerTest * * @package Mage_Catalog * @subpackage Mage_Catalog_Test * * * @uses PHPUnit_Framework_Magento_TestCase */class Mage_Catalog_IndexControllerTest extends Ibuildings_Mage_Test_PHPUnit_ControllerTestCase {
/** * theIndexActionShouldRedirectToRoot * @author Alistair Stead * @test */ public function theIndexActionShouldRedirectToRoot() { $this->dispatch('/'); $this->assertRoute('cms', "The expected cms route has not been matched"); $this->assertAction('index', "The index action has not been called"); $this->assertController('index', "The expected controller is not been used"); $this->assertQuery('div.nav-container', 'The site navigation is not present on the home page'); } // theIndexActionShouldRedirectToRoot}
Wednesday, 9 February 2011
/** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm
Wednesday, 9 February 2011
/** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm
Wednesday, 9 February 2011
/** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm
Wednesday, 9 February 2011
/** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm
Wednesday, 9 February 2011
/** * theAdminRouteAccessesTheAdminApplicationArea * @author Alistair Stead * @test */ public function theAdminRouteAccessesTheAdminApplicationArea() { $this->dispatch('admin/'); $this->assertRoute('adminhtml', "The expected route has not been matched"); $this->assertAction('login', "The login form should be presented"); $this->assertController('index', "The expected controller is not been used"); } // theAdminRouteAccessesTheAdminApplicationArea /** * theIndexActionDisplaysLoginForm * @author Alistair Stead * @group login * @test */ public function theIndexActionDisplaysLoginForm() { $this->dispatch('admin/index/'); $this->assertQueryCount('form#loginForm', 1); } // theIndexActionDisplaysLoginForm
Wednesday, 9 February 2011
/** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) );
$this->dispatch('admin/index/login');
$this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError
Wednesday, 9 February 2011
/** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) );
$this->dispatch('admin/index/login');
$this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError
Wednesday, 9 February 2011
/** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) );
$this->dispatch('admin/index/login');
$this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError
Wednesday, 9 February 2011
/** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) );
$this->dispatch('admin/index/login');
$this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError
Wednesday, 9 February 2011
/** * submittingInvalidCredsShouldDisplayError * @author Alistair Stead * @group login * @test */ public function submittingInvalidCredsShouldDisplayError() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => 'invalid-password', ) ) );
$this->dispatch('admin/index/login');
$this->assertQueryCount('li.error-msg', 1); $this->assertQueryContentContains('li.error-msg', 'Invalid Username or Password.'); } // submittingInvalidCredsShouldDisplayError
Wednesday, 9 February 2011
/** * submittingValidCredsShouldDisplayDashboard * @author Alistair Stead * @group login * @test */ public function submittingValidCredsShouldDisplayDashboard() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login');
$this->assertRedirect('We should be redirected after login'); $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard'); } // submittingValidCredsShouldDisplayDashboard
Wednesday, 9 February 2011
/** * submittingValidCredsShouldDisplayDashboard * @author Alistair Stead * @group login * @test */ public function submittingValidCredsShouldDisplayDashboard() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login');
$this->assertRedirect('We should be redirected after login'); $this->assertRedirectRegex("/^.*dashboard.*$/", 'We are not directed to the dashboard'); } // submittingValidCredsShouldDisplayDashboard
Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {
/** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset();
$this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders }
Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {
/** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset();
$this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders }
Wednesday, 9 February 2011
class Mage_Adminhtml_SalesControllerTest extends Mage_Adminhtml_ControllerTestCase {
/** * indexActionListsOrders * @author Alistair Stead * @test */ public function indexActionListsOrders() { $this->request->setMethod('POST') ->setPost( array( 'login' => array( 'username' => 'admin', 'password' => '123456', ) ) ); $this->dispatch('admin/index/login'); // Reset the requests after login before next dispatch $this->reset();
$this->dispatch('admin/sales_order/index'); $this->assertQueryContentContains('h3.icon-head', 'Orders'); } // indexActionListsOrders }
Wednesday, 9 February 2011
TESTING EMAIL CONTENTWho gets all the emails sent to [email protected]?
Wednesday, 9 February 2011
public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';
// Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); // Rewrite the core classes at runtime to prevent emails from being sent Mage::getConfig()->setNode('global/models/core/rewrite/email_template', 'Ibuildings_Test_Model_Email_Template'); // This is a hack to get the runtime config changes to take effect Mage::getModel('core/email_template'); }
Wednesday, 9 February 2011
public function mageBootstrap() { Mage::reset(); if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) { Mage::setIsDeveloperMode(true); } // Store or website code $this->mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';
// Run store or run website $this->mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store'; // Initialize the Mage App and inject the testing request & response Mage::app($this->mageRunCode, $this->mageRunType, $this->options); Mage::app()->setRequest(new Ibuildings_Mage_Controller_Request_HttpTestCase); Mage::app()->setResponse(new Ibuildings_Mage_Controller_Response_HttpTestCase); // Rewrite the core classes at runtime to prevent emails from being sent Mage::getConfig()->setNode('global/models/core/rewrite/email_template', 'Ibuildings_Test_Model_Email_Template'); // This is a hack to get the runtime config changes to take effect Mage::getModel('core/email_template'); }
Wednesday, 9 February 2011
/** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess
Wednesday, 9 February 2011
/** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess
Wednesday, 9 February 2011
/** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess
Wednesday, 9 February 2011
/** * submittingForgotPasswordWithValidEmailReturnsSuccess * @author Alistair Stead * @group password * @test * */ public function submittingForgotPasswordWithValidEmailReturnsSuccess() { $this->request->setMethod('POST') ->setPost(array('email' => $this->email)); $this->dispatch('admin/index/forgotpassword/'); $this->assertQueryCount('li.success-msg', 1); $this->assertQueryContentContains('li.success-msg', 'A new password was sent to your email address. Please check your email and click Back to Login.'); // Test that the email contains the correct data $emailContent = $this->getResponseEmail() ->getBodyHtml() ->getContent(); // Overriding the response body to be able to use the standard content assertions $this->response->setBody($emailContent); // The email content addresses the fixture user $this->assertQueryContentContains('body', "Dear $this->firstName $this->lastName"); // The fixture users password has been changed $this->assertNotQueryContentContains('body', $this->password); } // submittingForgotPasswordWithValidEmailReturnsSuccess
Wednesday, 9 February 2011
SO WHAT DO AUTOMATED TESTS GIVE US?
Wednesday, 9 February 2011
CODE COVERAGE
Wednesday, 9 February 2011
STATIC CODE ANALYSIS
Wednesday, 9 February 2011
CONTINUOUS INTEGRATION
Wednesday, 9 February 2011
Wednesday, 9 February 2011
Wednesday, 9 February 2011
SO WHAT NOW?
Wednesday, 9 February 2011
MAGE-TEST IS OPEN SOURCE
• Use Mage-Test to test your development projects
• Use Mage-Test to test you extensions
• Contribute tests for Magento
• Build up the coverage of the Magento codebase
• Take advantage of core tests for regression testing
Wednesday, 9 February 2011
MAGE-TESThttp://github.com/ibuildings/Mage-Test
Wednesday, 9 February 2011
THANK YOU!
• Email: [email protected]
• Skype: astead-ibuildings
• Twitter : @alistairstead
Wednesday, 9 February 2011
REFERENCES
• PHPUnit https://github.com/sebastianbergmann/phpunit/
• Zend_Test http://framework.zend.com/manual/en/zend.test.html
•Mage-Test https://github.com/ibuildings/Mage-Test
• phpUnderControl http://phpundercontrol.org/
• Bamboo http://www.atlassian.com/software/bamboo/
Wednesday, 9 February 2011
IMAGE CREDITS
BA Plane: http://www.flickr.com/photos/bribri/1299325208/sizes/l/in/photostream/
Minions: http://www.akblessingsabound.com/wp-content/uploads/2010/06/despicable-me-minions-blessings-abound-mommy.jpg
Eleventh Hour: http://www.flickr.com/photos/d4dee/2258343575/sizes/l/in/photostream/
Deep Thought: http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/
US Dollar : http://www.flickr.com/photos/8640416@N02/4213361072/sizes/l/in/photostream/
Whoa (Stop Sign): http://www.flickr.com/photos/aquaoracle/3265987824/sizes/l/
Departures Board: http://www.flickr.com/photos/scottmulhollan/4892422469/sizes/l/in/photostream/
Syringe: http://www.flickr.com/photos/woodypics/3809842998/sizes/z/in/photostream/
Coupled Carriages: http://www.flickr.com/photos/jowo/89657494/sizes/o/in/photostream/
Wednesday, 9 February 2011
QUESTIONS?
Wednesday, 9 February 2011
WE ARE HIRING!http://www.ibuildings.co.uk/about/careers/
Wednesday, 9 February 2011
Top Related