Testing Magento Modules with Aspect Mock

What is Aspect Mock?

Aspect mock is yet another PHP mocking framework, but unlike other mocking frameworks like Mockery or the default mock objects baked into PHPUnit it allows us to stub static calls which is what makes it perfect for testing Magento.

How does it work ?

Magic!

No… we’re joking but when you see it you can understand why it could be confused for magic. Instead it uses an amazing software development methodology called Aspect-Oriented Programming with the help of Go! AOP which simply put from its author…

Go! AOP is a modern aspect-oriented framework in plain PHP with rich features for the new level of software development framework allows to solve a cross-cutting issues in the traditional object-oriented PHP code by providing a highly efficient and transparent hook system for your existing code. – Alexander Lisachenko, via GitHub

The Go! AOP library does aspect weaving, patching autoloaded PHP classes on the fly (storing them in cache for future use) to add point cuts on every method allowing Aspect Mock to easily intercept any method call, even those annoying static ones!

Why do we need Aspect Mock?

We had a specific criteria that we just couldn’t meet until we found it.

Stub static calls

No PECL or Pear dependencies

Work with our Magento Skeleton

Must fit in with our current deployment process

Static calls

If you’re tried unit testing Magento modules you probably already know why we need to be able to stub static calls so badly. Most of Magento is pulled together using the class Mage. This class consists completely of static functions to do tasks such as loading models, helpers, layouts and config to message logging and of course initializing the whole application.

Simply put, stubbing static calls for the Mage class allows us to test different config dependant use cases and easily swap core models, helpers etc with mocked alternatives whilst asserting that these calls are being made with the expected values.

PECL, Pear our Workflow and Composer

Over the last year we made major inroads to creating a simple yet effective development workflow for Magento which I’m sure will be detailed in a future post.

The key to our workflow is using Composer to manage our dependencies including various Magento modules and third party libraries. What this allows us to do is easily deploy our codebase including dependencies in one smooth action whilst having a super simple local environment for development. We don’t have to worry about having PECL or Pear extensions installed for custom functionality and for this reason we avoid them completely, even for development purposes.

But someone must have implemented testing on Magento, surely?

Yes there have been various attempts at pushing unit tests into Magento userland, some of which are incomplete and most of which require spinning up a full instance of Magento. In most cases having a full instance of the Magento application running is overkill especially for standalone modules.

Using Aspect Mock for Testing Magento

We’ve done all the hard work here and created an open source Magento extension base which provides you an extension that is composer installable and test ready.

Getting Started

Just head on over to the GitHub repository, clone it or pull down the latest zip. Now in your favourite terminal application jump into the extension you just pulled down and run the build.php script.

For a quick setup just copy the commands below…

$ git clone https://github.com/wearejh/magento-extension-base your-ext-name $ cd your-ext-name $ php build.php

Follow the instructions and that’s it!, an initial commit will be made for your and your remote added. You can then run git push whenever you are ready.

Writing a Test with Aspect Mock

In the extension base we include a simple example that simply checks to ensure Mage::getModel(‘catalog/product’) returns our mocked object.

public function testModelCall() { // This test demonstrates how to create a test double for Mage // Normally you would be testing a separate classes method so // some of the logic below would be in a separate file, obvs. // Create a mock product object // Standard PHPUnit implementation $mockModel = $this ->getMockBuilder('Mage_Catalog_Model_Product') ->setMethods(array('getSku')) ->getMock(); // Explicitly return a SKU when // getSku() is called on the mock $mockModel ->expects($this->once()) ->method('getSku') ->will($this->returnValue('123456789')); // Stub Mage::getModel() // so it returns our mock object $modelStub = test::double('Mage', ['getModel' => $mockModel]); // This is the logic we would be testing $model = Mage::getModel('catalog/product'); $sku = $model->getSku(); // Our mock should have return this SKU $this->assertSame('123456789', $sku); // Ensure getModel stub was called $modelStub->verifyInvokedOnce('getModel', 'catalog/product'); }

Hopefully from this you can get a basic understanding of how we are unit testing Magento with Aspect Mock and how easy it is to integrate into your own modules. We look forward to you using our Magento extension base and any suggestions or contributions you have.