magento imagine ecommerce conference - february 2011 - unit testing with magento

97
QUALITY ASSURANCE IN MAGENTO EXTENSIONS Automated Functional Testing Wednesday, 9 February 2011

Upload: varien

Post on 10-May-2015

2.409 views

Category:

Documents


3 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

QUALITY ASSURANCE IN MAGENTO EXTENSIONS

Automated Functional Testing

Wednesday, 9 February 2011

Page 2: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 3: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

CREATING SOFTWARE IS COMPLEX

Wednesday, 9 February 2011

Page 4: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

CREATING SOFTWARE IS COMPLEX

(Maintaining it is arguably harder)

Wednesday, 9 February 2011

Page 5: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

FAULT TOLERANCE IS

LOW

Wednesday, 9 February 2011

Page 6: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

“Hello, technical support?”

“I would like to report a bug...”

Wednesday, 9 February 2011

Page 7: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

E-COMMERCE HAS A LOW TOLERANCE TO FAULTS.

Wednesday, 9 February 2011

Page 8: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MAGENTO MAKES IT OBVIOUS SOMETHING IS

WRONG

Wednesday, 9 February 2011

Page 9: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

With whom should the BUCK stop?

Wednesday, 9 February 2011

Page 10: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

With whom should the BUCK stop?

Lets make sure its not us....

Wednesday, 9 February 2011

Page 11: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

SO WHAT CAN WE DO TO FIX THIS?

Wednesday, 9 February 2011

Page 12: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

We are clever people right?

Wednesday, 9 February 2011

Page 13: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Just get more of us?

Wednesday, 9 February 2011

Page 14: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Testing at the eleventh hour...

Wednesday, 9 February 2011

Page 15: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

We can use technology

to help!

Wednesday, 9 February 2011

Page 16: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

AUTOMATED TESTING FRAMEWORKS CAN DO THE

HEAVY LIFTING.

Selenium Watir

Wednesday, 9 February 2011

Page 17: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

PHPUNITThe de-facto standard for unit testing in PHP projects

Wednesday, 9 February 2011

Page 18: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

WE CAN RUN UNIT TESTS FOR MAGENTO!

Wednesday, 9 February 2011

Page 19: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 20: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 21: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MODELS

Wednesday, 9 February 2011

Page 22: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 23: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 24: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 25: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 26: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 27: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 28: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 29: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 30: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

HELPERS

Wednesday, 9 February 2011

Page 31: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 32: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 33: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 34: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 35: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 36: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 37: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Okay we’re done lets go home...

Wednesday, 9 February 2011

Page 38: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Hold on, we have not really done anything new.

Wednesday, 9 February 2011

Page 39: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

DID WE MENTION FUNCTIONAL TESTING YET?

Wednesday, 9 February 2011

Page 40: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

QUALITY ASSURANCE IN MAGENTO EXTENSIONS

Automated Functional Testing

Wednesday, 9 February 2011

Page 41: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

ZEND_TEST?

Wednesday, 9 February 2011

Page 42: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MAGENTO IS CLOSELY COUPLED TO

MAGE_CORE_APPLICATION

Wednesday, 9 February 2011

Page 43: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Coupled components cause inflexibilityWednesday, 9 February 2011

Page 44: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Dependency injection is the key

Wednesday, 9 February 2011

Page 45: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MAGE-TESThttp://github.com/ibuildings/Mage-Test

Wednesday, 9 February 2011

Page 46: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 47: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MAGE_CORE_APPLICATIONLoaded from the community code pool

Wednesday, 9 February 2011

Page 48: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 49: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 50: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 51: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 52: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

CONTROLLER TESTSIbuildings_Mage_Test_PHPUnit_ControllerTestCase

Wednesday, 9 February 2011

Page 53: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 54: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 55: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 56: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Ibuildings_Mage_Controller_Response_HttpTestCase

Controller

Ibuildings_Mage_Controller_Request_HttpTestCase

View

Model

Ibuildings_Mage_Controller_Response_HttpTestCase

Wednesday, 9 February 2011

Page 57: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 58: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 59: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 60: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 61: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

<?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

Page 62: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 63: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 64: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 65: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 66: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 67: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 68: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 69: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 70: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 71: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 72: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 73: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 74: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 75: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 76: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 77: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

TESTING EMAIL CONTENTWho gets all the emails sent to [email protected]?

Wednesday, 9 February 2011

Page 78: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 79: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 80: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 81: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 82: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 83: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

/** * 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

Page 84: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

SO WHAT DO AUTOMATED TESTS GIVE US?

Wednesday, 9 February 2011

Page 85: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

CODE COVERAGE

Wednesday, 9 February 2011

Page 86: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

STATIC CODE ANALYSIS

Wednesday, 9 February 2011

Page 87: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

CONTINUOUS INTEGRATION

Wednesday, 9 February 2011

Page 88: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Wednesday, 9 February 2011

Page 89: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

Wednesday, 9 February 2011

Page 90: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

SO WHAT NOW?

Wednesday, 9 February 2011

Page 91: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 92: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

MAGE-TESThttp://github.com/ibuildings/Mage-Test

Wednesday, 9 February 2011

Page 93: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

THANK YOU!

• Email: [email protected]

• Skype: astead-ibuildings

• Twitter : @alistairstead

Wednesday, 9 February 2011

Page 94: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 95: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

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

Page 96: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

QUESTIONS?

Wednesday, 9 February 2011

Page 97: Magento Imagine eCommerce Conference - February 2011 - Unit Testing with Magento

WE ARE HIRING!http://www.ibuildings.co.uk/about/careers/

Wednesday, 9 February 2011