Testing the quality of Lesti_Fpc

Lesti_Fpc has changed in the last month. Some of the changes are part of the code quality. Today I would like to present the tools, that I use to raise the quality of my Magento extension.

Continuous Integration

A Continuous integration is pretty important. It runs my tests and code sniffers after every push. Travis CI is the top dog for open source projects. Travis CI is a continuous integration platform for open source projects. I just added a .travis.yml to the root of my Github Repository.

language: php
php:
  - 5.3
  - 5.4
env:
  - MAGE=magento-1-9-0-1
  - MAGE=magento-1-8-1-0
  - MAGE=magento-1-7-0-2
  - MAGE=magento-1-6-2-0
  - MAGE=magento-1-5-1-0
install:
  - cp -f .travis/composer.json composer.json
  - composer install --dev
  - curl https://gordonlesti.com/"$MAGE".tar.gz | tar xz
  - mv $MAGE magento
  - cd magento
  - wget https://raw.githubusercontent.com/colinmollenhour/modman/master/modman
  - chmod +x modman
  - ./modman init
  - cd ..
before_script:
  - mkdir -p build/logs
  - mysql -e 'create database `magento`;'
  - mysql -utravis magento < magento/var/magento.sql
  - mysql -e 'create database `magento_test`;'
  - mysql -utravis magento_test < magento/var/magento_test.sql
  - cd magento
  - ./modman link ./../../Lesti_Fpc
  - ./modman clone https://github.com/EcomDev/EcomDev_PHPUnit.git
  - cd ..
script:
  - php vendor/bin/phpcs --standard=Zend app/
  - php vendor/bin/phpmd app/ text codesize,design,unusedcode
  - cd magento && php ../vendor/bin/phpunit --coverage-clover ../build/logs/clover.xml && cd ..
after_script:
  - php vendor/bin/coveralls
As you can see, I did run my tests against PHP 5.3 and 5.4. The tested Magento versions are 1.5, 1.6, 1.7, 1.8 and 1.9. I have created blank Magento shops with the super awesome tool n98-magerun. After that I have deleted the media folder, created mysql dumps of the shop and the testing database. The two databases and the code are compressed in the following files: Travis downloads those files before the tests start and installs the Magento shop. I guess I can replace this Magento installation construction with MageCI in the future.

Composer

I use Composer to installing all that testing and quality tools. Here is my .travis/composer.json

{
  "require-dev": {
    "phpunit/phpunit": "*",
    "phpmd/phpmd" : "*",
    "squizlabs/php_codesniffer": "*",
    "satooshi/php-coveralls": "dev-master"
  }
}

Code Sniffer and Mess Detector

In front of the unit tests, I did check if the code ensures the common styling guidelines. I did use PHPCS with the Zend coding standard. This is a pretty old standard, but it mostly fits the coding style of Magento. And that is the important part for other developers, if they maintain my code. Maybe I will change to the coding standard of Firegento in the future. With PHPMD I did check things like unused variables, maximum number of methods in a class and so on.

Unit Tests

To test an PHP project, you should use PHPUnit. But in Magento it's damm hard to test. Out of this reason, Ivan Chepurnyi has created EcomDev_PHPUnit. It will help you to write unit tests for Magento. I did install EcomDev_PHPUnit with modman from Colin Mollenhour. Just as example, here is app/code/community/Lesti/Fpc/Test/Helper/Data.php to test app/code/community/Lesti/Fpc/Helper/Data.php.

<?php
/**
 * Lesti_Fpc (http:gordonlesti.com/lestifpc)
 *
 * PHP version 5
 *
 * @link      https://github.com/GordonLesti/Lesti_Fpc
 * @package   Lesti_Fpc
 * @author    Gordon Lesti <info@gordonlesti.com>
 * @copyright Copyright (c) 2013-2014 Gordon Lesti (http://gordonlesti.com)
 * @license   http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 */

/**
 * Test case for Lesti_Fpc_Helper_Data
 */
class Lesti_Fpc_Test_Helper_Data extends Lesti_Fpc_Test_TestCase
{
    /**
     * @var Lesti_Fpc_Helper_Data
     */
    protected $_helper;

    protected function setUp()
    {
        parent::setUp();
        $this->_helper = Mage::helper('fpc');
    }

    /**
     * @test
     * @loadFixture get_cacheable_actions.yaml
     */
    public function testGetCacheableActions()
    {
        $this->assertEquals(
            array('cms_index_index', 'cms_page_view', 'catalog_product_view'),
            $this->_helper->getCacheableActions()
        );
    }

    /**
     * @test
     * @loadFixture get_cacheable_actions_empty.yaml
     */
    public function testGetCacheableActionsEmpty()
    {
        $this->assertEquals(array(), $this->_helper->getCacheableActions());
    }

    /**
     * @test
     * @loadFixture get_bypass_handles.yaml
     */
    public function testGetBypassHandles()
    {
        $this->assertEquals(
            array('some_handle', 'logged_in', 'CATEGORY_25'),
            $this->_helper->getBypassHandles()
        );
    }

    /**
     * @test
     * @loadFixture get_bypass_handles_empty.yaml
     */
    public function testGetBypassHandlesEmpty()
    {
        $this->assertEquals(array(), $this->_helper->getBypassHandles());
    }

    /**
     * @test
     * @loadFixture get_refresh_actions.yaml
     */
    public function testGetRefreshActions()
    {
        $this->assertEquals(
            array(
                'checkout_cart_add',
                'checkout_cart_delete',
                'checkout_cart_updatePost'
            ),
            $this->_helper->getRefreshActions()
        );
    }

    /**
     * @test
     * @loadFixture get_refresh_actions_empty.yaml
     */
    public function testGetRefreshActionsEmpty()
    {
        $this->assertEquals(array(), $this->_helper->getRefreshActions());
    }

    /**
     * Test that URI params can be matched by RegEx
     *
     * @test
     * @loadFixture regex_uri_params.yaml
     * @dataProvider dataProvider
     */
    public function testRegexUriParams(
        $uriParamSets = array(),
        $expectedCount = 0
    )
    {
        $actualKeys = array();
        foreach ($uriParamSets as $uriParams) {
            $this->_resetParams();
            Mage::app()->getRequest()->setParams($uriParams);
            $actualKeys[] = $this->_helper->getKey();
        }
        $this->assertCount(
            $expectedCount,
            array_unique($actualKeys),
            sprintf('%d different keys expected', $expectedCount)
        );
    }

    /**
     * @test
     * @loadFixture can_cache_request.yaml
     * @dataProvider dataProvider
     */
    public function testCanCacheRequest($method, $expected, $params = array())
    {
        Mage::app()->getRequest()->clearParams();
        Mage::app()->getRequest()->setMethod($method);
        Mage::app()->getRequest()->setParams($params);
        $this->assertEquals(
            (bool) $expected,
            $this->_helper->canCacheRequest()
        );
    }

    /**
     * @test
     */
    public function getFullActionName()
    {
        Mage::app()->getRequest()->setRouteName('f');
        Mage::app()->getRequest()->setControllerName('p');
        Mage::app()->getRequest()->setActionName('c');
        $this->assertEquals('f_p_c', $this->_helper->getFullActionName());
    }

    /**
     * Reset parameters in Magento request and FPC
     */
    protected function _resetParams()
    {
        if (Mage::registry(Lesti_Fpc_Helper_Data::REGISTRY_KEY_PARAMS)) {
            Mage::unregister(Lesti_Fpc_Helper_Data::REGISTRY_KEY_PARAMS);
        }
        Mage::app()->getRequest()->clearParams();
    }
}

Code Coverage

PHPUnit creates coverage clover files, that contain information about my code coverage. Just to be sure, that the code coverage increases, I did use Coveralls. This service for open source projects creates comments on pull requests, if the code coverage increased or decreased.

Next Previous