Extension developers, test your extensions

This post isn’t about advanced testing techniques like a Test Driven Development, an unit testing or a functional testing. It’s just about spending some time to USE an extension you wrote to make sure it works in different cases.

I prepared a subjective list of things you should check when you develop a Magento extension.

1. Multiple stores mode

Make sure to test your extension in Magento setup with only one default store in use as well as with multiple stores. Check code around isSingleStoreMode() method for reference.

It’s not only about making sure that your entity (eg. a blog post) can be set up to work only for a particular store view. There is much more:

  • do you access configuration directives properly? Make sure to provide a store id when you access a configuration directive which is allowed to set per website or store view. It’s a common problem when a developer allows to configure an extension for a store scope view (show_in_store = 1), and then forgets to provide store id as a second parameter to Mage::getStoreConfig() method when he accesses configuration directives. If your extension works only in frontend, it’s not as bad. If it works in backend or through cron, it may lead to problems.
  • do you use a store id coming from the entity you are working on instead of using one from a Mage class? A common problem I saw was allowing to configure a payment gate per store scope and then using Mage::app()->getStore() in Mage::getStoreConfig() calls. It likely won’t work when you try to trigger payment using such payment method in backend. Use getStoreId() of your order object model instead.

2. Flat catalog enabled

Test your extension with flat mode enabled and disabled (System -> Configuration -> Catalog -> Catalog -> Frontend -> Use Flat Catalog Category/Product).

If an attribute is not enabled for flat mode (“Used in Product Listing” set to “No” in an attribute form), it could be used to filter a collection using addFieldToFilter() method only if specific method call is used. In other case it will be silently ignored and filtering won’t be applied at all.

This will work:

$collection->addFieldToFilter(
    array(array('attribute' => 'attribute_code', 'eq' => 1)
)

But this won’t:

$collection->addAttributeToFilter('attribute', array('eq' => 1));

If the second one is used and attribute is not enabled for use in flat mode, it will be ignored and the filter won’t be applied at all.

Also, keep in mind that the index table contains only products of status enabled so you won’t be able to get disabled products using a standard catalog/product collection model when flat mode is enabled. If you want to get a collection of disabled products in frontend and make sure it works in both flat mode states, you may need to ditch the standard way of getting a product collection which is:

$collection = Mage::getModel('catalog/product')->getCollection();

and get EAV collection using:

$collection = Mage::getResourceModel('catalog/product_collection');

3. Different product types

Products of complex Magento product types are stored in the cart and other order entities in different, specific ways. This should be accounted in any integration code which operates on order or cart data like: shipping estimator extensions, payment method gates connectors, email marketing modules and many more. Also, product types may affect on what entities order will contain.

  • order which contains only virtual products doesn’t have a shipping address. Does your UI cover for that? Be careful when your code relies on the shipping address and check if it exists in order before you try to get access to it.
  • Magento allows to set up different bundle products containing items with the same SKU. It may result in having multiple line items with the same SKU in the cart. Does your integration code or external service support that?
  • Bundle and Configurable parent products are stored in the cart object along with all child items. You need to account for that in any code which relies on cart or order items. One of the possible solutions is to ignore parent item in such integration but…
  • … a configurable parent product carries the total product price and price for child item stored in cart is $0. If configurable parent is simply ignored, price will be lost. A similar behavior occurs for a bundle parent product when it is set up in “fixed” price mode.

This shows that any code should be tested with multiple product types. Additionally for bundle products it should be tested for both “static” and “dynamic” modes of SKU, weight and price attributes. Testing an extension with default Magento demo data won’t be enough as demo data contains only bundle products set with “dynamic” mode for these attributes.

4. Different checkout types

Test your code with different checkout setups. I’ve already mentioned checkout without a shipping address when only virtual products exist in cart. There are more possible use cases around checkout:

  • Does your extension work for Multiple Addresses checkout? If you write an extension which does anything with checkout, try to enable multishipping by changing System -> Configuration -> Sales -> Shipping Settings -> Options -> Allow Shipping to Multiple Addresses to “Yes” and check if it still works. A common example may be an extension which allows a customer to subscribe to a newsletter during checkout.
  • Try to go through one page checkout as a guest, as a customer logged in and as a customer who creates an account during checkout. For example, when you rely on address fields, keep in mind that the logged customer who uses an existing address won’t see any address forms by default. Example about a newsletter subscription I mentioned previously may also be the case here.

It’s also good to test edit cart workflows if your extension:

  • customizes products by modifying product type logic
  • relies on custom options
  • changes custom options behavior
  • modifies product page logic

Make sure you are able to add such customized product to the cart and then try to edit the product by using “edit” link in cart page.

5. Different environments

While working on a particular development environment it’s common to forget about other environments which can be used to run a Magento store.

  • altough Magento doesn’t offically support operating systems other than Linux in a production mode, they can be used for development purposes. Think about people using other systems and use DIRECTORY_SEPARATOR (or Magento specific shortcut, DS) when you write code which uses directory paths.
  • similar to previous point, you may also avoid hardcoding line ending characters and use PHP_EOL instead
  • don’t use new PHP goodies if you write an extension supporting older Magento versions. For example, if your extension is going to support Magento 1.8 which can use PHP old as 5.3.24, forget about using traits, short array syntax, function array dereferencing or other stuff introduced in PHP 5.4 and newer ones.
  • check if your code works with a case sensitive and case insensitive system. Problems may occur when you use lowercased model name part in Mage::getModel() method and camelCased file name. For example Mage::getModel(“module/foobar”) will resolve Module/Model/FooBar.php file on an OS X, but it won’t work on a Linux. Remember two things then: always access your files in case sensitive way and never store two files or directories with the same name and two different casings in one directory.
  • test your extension using different browsers and devices. It’s really easy way to make sure that your extension works regardless of OS and web browser used.
  • make sure to not use non standard PHP functions in your extension code. For example, I saw an example of using a locale_get_primary_language() function in custom extension code. This function doesn’t exist in default PHP installation and it requires installing intl package. This library needs to be installed system wide and it doesn’t work in all cases. It should be safe to use the Zend_Locale from Zend Framework which is shipped with Magento to achieve the same goal.

6. Cache enabled

Custom extension code rather works with disabled cache, but does it still work with cache enabled? Try to enable cache and test the extension.

Main problems can occur around block caches and full page cache. If you plan to support Magento Enterprise Edition, make sure to test your extension with a Full Page Cache enabled then.

7. Compilation enabled

Make sure to test if your extension works with PHP code compilation enabled.

It could be done in a System -> Tools -> Compilation. First compile your code using “Run Compilation Process” button, then enable use of compiled files and go through your extension features to make sure everything works fine.

If you spot any errors, check require_once statements in your custom code. They are the main cause of problems around code compliation and they are actually not needed in Magento and Zend Framework auto loading.

I don’t debate if compilation should be enabled or not, but for sure it’s good to write an extension which works in both cases.

8. JavaSvript and CSS merging enabled

Enable CSS and JavaScript merging in a System -> Configuration -> Developer and check frontend with error console open eg. Firebug. Make sure that all frontend features still works without throwing warning or error into console. CSS after merging will likely work, however JavaScript merging can break site in some cases.

Tip: in many cases adding a semicolon at the beginning and at the end of JavaScript file does the trick.

Keep in mind that if merging CSS and JavaScript is enabled in Global configuration scope, all styles and scripts in backend will be merged too. Test backend then as well.

9. Custom backend URL

Magento can be set up to use custom backend URL as well as it can use custom domain for backend. Make sure to test your extension with custom backend URL set what can be done in app/etc/local.xml file. Also test your extension with custom domain used for admin backend what could be set in System -> Configuration -> Advanced -> Admin -> Admin Base URL -> Use Custom Admin URL.

Also, test extension with enabled adding secret keys to admin backend URLs in System -> Configuration -> Advanced -> Admin -> Security. If you send any links pointing to admin backend pages in email communications, check if they still work with that feature enabled.

10. Different error handling settings

Magento uses E_ALL | E_STRICT error reporting level by default. It means that it will report any warnings, notices or strict notices found in the code. However, many of these problems won’t break Magento application immediately and they could be overlooked if logs are not being watched during development and testing.

To avoid that, test your extension with developer mode enabled. To do this, set following constant in .htaccess or domain configuration file:

SetEnv MAGE_IS_DEVELOPER_MODE 1

This will throw exception whenever notice, warning or other error occurs. This makes it harder to overlook such problems.

Note: Just keep in mind that when developer mode is enabled, Magento won’t run SQL upgrade scripts automatically.

Wrap up

In general, try to use your extension from different perspectives. Think what setup people can use. And all these shown above are really common, they are not edge use cases.

From my experience even big Magento extensions providers and companies who provide extensions to integrate with their service missed the above points.

Making sure that your extensions works based on these points will improve quality of your extension and chance that it works on different Magento setups will be much bigger.

What else do you test before you publish your extension?

Leave a Comment

Your email address will not be published. Required fields are marked *