20
Using of Test Driven Development Practices for Magento Ivan Chepurnyi Magento Trainer / Lead Developer

Using of TDD practices for Magento

Embed Size (px)

DESCRIPTION

My Presentation from MeetMagento NL 2011

Citation preview

Page 1: Using of TDD practices for Magento

Using of Test Driven DevelopmentPractices for Magento

Ivan ChepurnyiMagento Trainer / Lead Developer

Page 2: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Short Overview

Write a test

Run the tests

Fail

Pass

Write the code

Run the tests

Fail

Pass

1. Imaging how your feature should work and write a failing test

2. Write feature quickly for receiving passed test

3. Refactor your code and run the test again

Page 3: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Conventional Development

• You need to write a full functionality for seeing the result• You think that your code works• Debugging• With every fixed issue you may produce a new one• Even if you write unit tests after implementation it doesn’t

guarantee that you detect the defect.

Page 4: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Test Driven Development

• You don’t need to implement full functionality for seeing the result

• Your colleagues can use your test as learning material• The test proofs that your code works and can be verified• Serious defects can be fixed on early state

Page 5: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Defect Costs

• Defect found at the coding stage

• Defect found during QA phase

• Defect found after going live

Percentage from project development hours

< 1%

20%

> 40%

Page 6: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Type of Tests

• Automated Test• Regression Test• Learning Test• Integration Test

Page 7: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

• Test in Isolation

• Make test simple

• Test erroneous situations

• Test Doubles (Fake heavy resources you don’t depend on)

Main Principles

Page 8: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

EcomDev_PHPUnit

• Making possible isolation of your test

• Easy test data loading via Yaml fixtures

• Data providers and expectations for reusable tests

• Easy way of testing configuration files

• Easy way for Layouts & Controllers integration test

Page 9: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Simple Test Case

class EcomDev_Example_Test_Model_Product extends EcomDev_PHPUnit_Test_Case

{/** * @test * @loadFixture * @dataProvider dataProvider */ public function priceCalculation($productId, $storeId) { $storeId = Mage::app()->getStore($storeId)->getId(); $product = Mage::getModel('catalog/product') ->setStoreId($storeId) ->load($productId);

$expected = $this->expected('%s-%s', $productId, $storeId); $this->assertEquals($expected->getFinalPrice(), $product->getFinalPrice());

$this->assertEquals($expected->getPrice(), $product->getPrice()); }}

Test Case Class

Page 10: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Simple Test Case

eav:    catalog_product:     - entity_id: 1        type_id: simple       sku: book          website_ids:          - usa_website          - canada_website          price: 12.99        status: 1  # Enabled        visibility: 4  # Visible in Catalog & Search        /websites:  # Set different prices per website         usa_website:            special_price: 9.99          german_website:            price: 9.99            special_price: 5.99

Yaml Fixture

Page 11: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Simple Test Case

1-2: # Product=Book Store=USA

final_price: 9.99

price: 12.99

1-3: # Product=Book Store=Canada

final_price: 12.99

price: 12.99

Yaml Expectation

- - 1 - usa- - 1 - canada- - 1 - germany

Yaml Data Provider

Page 12: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Event Dispatch Check

class EcomDev_Example_Test_Model_Cms_Page extends EcomDev_PHPUnit_Test_Case

{

// …

public function testAvailableStatuses() {

Mage::getModel(‘cms/page’)->getAvailableStatuses();

$this->assertEventDispatched(

‘cms_page_get_available_statuses’

);

}

}

Test Case

Page 13: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Test Doubles

class EcomDev_PHPUnit_Tes_Case_Controllerextends EcomDev_PHPUnit_Test_Case

{protected function registerCookieStub()

{ $cookie = $this->getModelMock('core/cookie', array('set', 'delete')); $cookie->expects($this->any()) ->method('set') ->will($this->returnCallback(array($this, 'setCookieCallback‘)));

$cookie->expects($this->any()) ->method('delete‘) ->will($this->returnCallback(array($this, 'deleteCookieCallback‘)));

$this->replaceByMock('singleton', 'core/cookie', $cookie); return $this; }

}

Test Case

Page 14: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Config Test Case

class EcomDev_Example_Test_Config_Main extends EcomDev_PHPUnit_Test_Case_Config

{

//….

public function testModuleVersion()

{

$this->assertModuleCodePool('local');

$this->assertModuleDepends(‘Mage_Catalog’);

$this->assertModuleVersionGreaterThan(‘0.1.0');

}

}

Testing Module Nodes

Page 15: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Config Test Case

class EcomDev_Example_Test_Config_Main extends EcomDev_PHPUnit_Test_Case_Config

{

//….

public function testClassAliasDefinitions()

{

$this->assertModelAlias('catalog/product', 'Mage_Catalog_Model_Product');

$this->assertResourceModelAlias(

'catalog/product',

‘Mage_Catalog_Model_Resource_Eav_Mysql4_Product‘

);

$this->assertBlockAlias(

'catalog/product_list',

'Mage_Catalog_Block_Product_List‘

);

}

}

Testing Class Aliases

Page 16: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Config Test Case

class EcomDev_Example_Test_Config_Main extends EcomDev_PHPUnit_Test_Case_Config

{

//….

public function testEventObservers()

{

$this->assertEventObserverDefined(

'frontend', 'customer_login',

'catalog/product_compare_item',

'bindCustomerLogin'

);

}

}

Testing Event Observer Definitions

Page 17: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Controller Test Case

class EcomDev_Example_Test_Controller_Main extends EcomDev_PHPUnit_Test_Case_Controller

{

public function testRequest()

{

$this->dispatch('cms');

$this->assertRequestDispatched();

$this->assertRequestNotForwarded();

$this->assertRequestRoute('cms/index/index');

$this->assertRequestRouteName('cms');

$this->assertRequestControllerName('index');

$this->assertRequestControllerModule('Mage_Cms');

$this->assertRequestActionName('index');

}

}

Testing Request

Page 18: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

Controller Test Case

class EcomDev_Example_Test_Controller_Main

extends EcomDev_PHPUnit_Test_Case_Controller

{

public function testLayout()

{

$this->dispatch('');

$this->assertLayoutHandleLoaded('cms_index_index');

$this->assertLayoutBlockCreated('right');

$this->assertLayoutBlockRendered('content');

$this->assertLayoutBlockActionNotInvoked(

'footer_links', 'addLink', '', array('Custom Title')

);

$this->assertLayoutBlockActionInvokedAtLeast(

'footer_links', 'addLink', 4, '‘

);

}

}

Testing Layouts

Page 19: Using of TDD practices for Magento

Ivan Chepurnyi Meet Magento Netherlands

What’s Next

• Write automated tests for your moduleshttp://www.magentocommerce.com/magento-connect/Ecommerce%20Developers/extension/5717/ecomdev_phpunit

• Keep project healthy during its lifecycle with Continuous Integration

• Running Daily Builds• Running Unit Tests in 10 minutes after last commit

– Hudson http://hudson-ci.org– phpUnderControl http://phpundercontrol.org/

Page 20: Using of TDD practices for Magento

Questions?

[email protected]