TDD & Continuous Integration – with Magento – Test Driven Development



TDD & Continuous Integration – with Magento – Test Driven Development

0 1


tdd-ci-mageday2013

TDD & Continuous Integration with Magento talk for Mage Day 2013

On Github pug-more / tdd-ci-mageday2013

TDD & Continuous Integration

with Magento

Manuele Menozzi Senior PHP Developer @ Webgriffe® Proud GrUSP & PUG MoRe member First Magento extension written in the 2009 Email: mmenozzi@webgriffe.com / Twitter: @mmenozzi

Test Driven Development

The repetition of a very short development cycle

Test Driven Development

benefits

  • You can write small feature's subsets (don't worry about all the complexity)
  • All features are tested
  • You reduce bugs and regressions rate
  • Your code stays clean
  • Your code is documented
  • And last but not least, you're more confident in making changes!

Test Driven Development

In PHP

PHPUnit github.com/sebastianbergmann/phpunit/

Unit Tests with Magento

Magento => Hardcoded dependencies => Testing hell

class Vendor_Module_Model_Something extends Mage_Core_Model_Abstract
{
    public function doSomethingWithProduct($productId)
    {
        $product = Mage::getModel('catalog/product')->load($productId);
        //do something with $product
    }
}

Unit Tests with Magento

We have to refactor, for example with dependency injection…

class Vendor_Module_Model_Something extends Mage_Core_Model_Abstract
{
    protected $productModel;

    public function setProductModel(
        Mage_Catalog_Model_Product $productModel
    ) {
        $this->productModel = $productModel;
    }

    public function doSomethingWithProduct($productId)
    {
        $product = $this->productModel->load($productId);
        //do something with $product
    }
}

Unit Tests with Magento

… so we can test it with something like this:

class Vendor_Module_Tests_Unit_SomethingTest extends
    \PHPUnit_Framework_TestCase
{
    public function testDoSomethingWithProduct()
    {
        $productModelMock = $this->getMock('Mage_Catalog_Model_Product');
        $productModelMock->expects($this->once())
            ->method('load')
            ->with(1)
            ->will($this->returnValue(new Mage_Catalog_Model_Product()));

        $somethingModel = new Vendor_Module_Model_Something();
        $somethingModel->setProductModel($productModelMock);
        $somethingModel->doSomethingWithProduct(1);
        // assert that something has been done
    }
}

Integration & Functional tests with Magento

EcomDev_PHPUnit

  • Magento extension
  • Doesn't change core files
  • Separate database connection is used for tests
  • Different levels of test integration

EcomDev_PHPUnit

Enable an extension for testing

<!-- app/code/local/Vendor/Module/etc/config.xml -->
<phpunit>
    <suite>
        <modules>
            <Vendor_Module />
        </modules>
    </suite>
</phpunit>

EcomDev_PHPUnit

Different levels of test integration

  • EcomDev_PHPUnit_Test_Case is for testing models, blocks and helpers
  • EcomDev_PHPUnit_Test_Case_Config is for testing your module configuration
  • EcomDev_PHPUnit_Test_Case_Controller is for testing your controller actions and layout rendering process

EcomDev_PHPUnit

  • Fixtures support (YAML files, database purge)
  • Expectations support (YAML files)
  • Data providers support (YAML files)
  • Indexing control (@doNotIndex & @doNotIndexAll)
  • Useful test doubles getModelMock(), getResourceModelMock(), getBlockMock(), getHelperMock(), replaceByMock(), …
  • Useful assertions assertEventDispatched(), assertEventDispatchedExactly(), assertEventDispatchedAtLeast(), …

EcomDev_PHPUnit

Configuration testing

Many useful configuration assertions:

  • Class Alias Assertions assertBlockAlias(), assertModelAlias(), assertHelperAlias(), …
  • Module Assertions assertModuleCodePool(), assertModuleDepends(), assertModuleVersion(), …
  • Layout Assertions assertLayoutFileDefined(), assertLayoutFileExists(), assertLayoutFileExistsInTheme(), …
  • Event Observer Assertions assertEventObserverDefined(), assertEventObserverNotDefined(), …
  • Config Node Assertions assertConfigNodeHasChild(), assertConfigNodeHasChildren(), assertConfigNodeContainsValue(), …

EcomDev_PHPUnit

Controller testing

  • Dispatch of controller actions $this->dispatch('customer/account/create');
  • Response object handling & assertions getSentHeaders(), getOutputBody(), assertResponseHttpCode(), assertRedirect(), …
  • Layout assertions assertLayoutLoaded(), assertLayoutBlockCreated(), assertLayoutBlockTypeOf(), assertLayoutBlockParentEquals(), assertLayoutBlockAfter(), …
  • Routing assertions assertRequestDispatched(), assertRequestRoute(), assertRequestControllerName(), …

Continuous Integration

What is it?

Continuous integration (CI) is the practice, in software engineering, of merging all developer working copies with a shared mainline several times a day.

Continuous Integration

Why do it?

  • Reduce risk
  • Reduce costs
  • Confidence
  • Tested builds
  • Reduce repetitive tasks
  • Quality assurance

Continuous Integration

Requirements

  • Source code repository
  • Automated self-testing build
  • Every commit triggers a build
  • [ Automated deploy after every successful build ]

Magento module Continuous Integration

We have to:

  • Download and install a specific version of Magento
  • Install the module
  • Install EcomDev_PHPUnit
  • Run tests
  • Repeat the above steps for other Magento versions

Magento module Continuous Integration

We can do it with MageCI by EcomDev (github.com/EcomDev/MageCI):

$ mage-ci install $mage_dir $mage_version $dbname -u $dbuser -p $dbpass \
    -o -c -t -r http://mage-ci.ecomdev.org
$ mage-ci install-module $mage_dir .
$ mage-ci install-module $mage_dir \
    https://github.com/EcomDev/EcomDev_PHPUnit.git
$ mage-ci phpunit $mage_dir

Magento module Continuous Integration

Jenkins Example

jenkins-ci.org

  • Put previous commands in a bash script or an ANT build
  • Multi-configuration job on Jenkins
  • User-defined axis -> mage_version = 1.5.1.0, 1.6.2.0, 1.7.0.2, [...]

Magento module Continuous Integration

Jenkins Example

?