Страницы

Monday, 5 June 2017

Zend Framework 2 Controller Tests optimization

Hi all!

You probably know about Controller tests described in Zend Framework (ZF) manual.

There is a known problem with memory usage in Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase. It is allocated about 10 Mb for each test in PHP 5.6 or 6 Mb in PHP 7.1.

Upgrading to the last PHP version is the first step of optimization. In that case you will save 40% of memory. Also this upgrade is required for the next optimization step.

Let see results for PHP 7.1:

PHPUnit 5.7.19 by Sebastian Bergmann and contributors.

.............................................................     61 / 61 (100%)

Time: 52.07 seconds, Memory: 364.00MB

OK (61 tests, 546 assertions)

For PHP 5.6 it will use about 606.00MB. Sorry, but now I haven't possibility to execute tests using PHP 5.

But the main problem is not fixed. In the future, it will require 3.5 Gb of memory in case when there will be 610 (10x more) tests. Why this happens? For each test is created new Zend Application and it is destroyed only when all tests are executed (PHP process will die)!

Seems Application is not destroyed due to linked dependencies in Service Manager (SM) that does not allow Garbage Collector (GC) remove already not used objects. But some improvements in GC that was introduces in PHP 7 made possibility for next optimization.

Lets create our own AbstractHttpControllerTestCase with unlinking all services from SM in tearDown method:
<?php

namespace Test\PHPUnit\TestCase;

use Test\PHPUnit\Bootstrap;
use Zend\ServiceManager\ServiceManager;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase as ZendAbstractHttpControllerTestCase;

abstract class AbstractHttpControllerTestCase extends ZendAbstractHttpControllerTestCase
{
    /**
     * @inheritdoc
     */
    public function tearDown()
    {
        parent::tearDown();

        /** @var ServiceManager $serviceManager */
        $serviceManager = $this->getApplication()->getServiceManager();

        $serviceNames = $serviceManager->getRegisteredServices();
        foreach ($serviceNames['invokableClasses'] as $serviceName) {
            $serviceManager->setInvokableClass($serviceName, '');
        }
        foreach ($serviceNames['factories'] as $serviceName) {
            $serviceManager->setFactory($serviceName, '');
        }
        foreach ($serviceNames['instances'] as $serviceName) {
            $serviceManager->setService($serviceName, []);
        }
        $serviceManager->setCanonicalNames([]);

        $this->reset();
    }
}

Now we can apply extended AbstractHttpControllerTestCase for all existed tests and check result.

PHPUnit 5.7.19 by Sebastian Bergmann and contributors.

.............................................................     61 / 61 (100%)

Time: 54.26 seconds, Memory: 62.00MB

OK (61 tests, 546 assertions)


How you can see is used only 62.00 Mb of memory, and this number will not changed even if there will be 10x more tests!


That's all!

Pavlo Chipak

No comments:

Post a Comment