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