├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── IgnoreTestPolicy.php └── TestListener.php └── tests └── TestListenerTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | composer.phar 3 | bin/ 4 | vendor/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - $HOME/.composer 8 | 9 | before_script: 10 | - phpenv config-rm xdebug.ini || true 11 | - composer self-update 12 | - composer require phpunit/phpunit:${PHPUNIT} 13 | 14 | script: 15 | - ./bin/phpunit 16 | 17 | matrix: 18 | include: 19 | - php: 7.1 20 | env: PHPUNIT=~7 21 | - php: 7.2 22 | env: PHPUNIT=~7 23 | - php: nightly 24 | env: PHPUNIT=~7 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2017 MyBuilder Limited 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHPUnit Accelerator 2 | 3 | [![Build Status](https://secure.travis-ci.org/mybuilder/phpunit-accelerator.svg?branch=master)](http://travis-ci.org/mybuilder/phpunit-accelerator) 4 | 5 | Inspired by [Kris Wallsmith faster PHPUnit article](http://kriswallsmith.net/post/18029585104/faster-phpunit), we've created a [PHPUnit](http://phpunit.de) test listener that speeds up PHPUnit tests about 20% by freeing memory. 6 | 7 | ## Installation 8 | 9 | To install this library, run the command below and you will get the latest version 10 | 11 | ``` bash 12 | composer require mybuilder/phpunit-accelerator --dev 13 | ``` 14 | 15 | ## Usage 16 | 17 | Just add to your `phpunit.xml` configuration 18 | 19 | ```xml 20 | 21 | 22 | 23 | 24 | 25 | ``` 26 | 27 | ### Ignoring Tests 28 | 29 | Sometimes it is necessary to ignore specific tests, where freeing their properties is undesired. For this use case, you have the ability to *extend the behaviour* of the listener by implementing the `IgnoreTestPolicy` interface. 30 | 31 | As an example, if we hypothetically wanted to ignore all tests which include "Legacy" in their test filename, we could create a custom ignore policy as follows 32 | 33 | ```php 34 | getFilename(), 'Legacy') !== false; 41 | } 42 | } 43 | ``` 44 | 45 | And pass it to the constructor of our test listener in `phpunit.xml` configuration 46 | 47 | ```xml 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | --- 60 | 61 | Created by [MyBuilder](http://www.mybuilder.com/) - Check out our [blog](http://tech.mybuilder.com/) for more insight into this and other open-source projects we release. 62 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mybuilder/phpunit-accelerator", 3 | "description": "PHPUnit accelerator", 4 | "keywords": ["phpunit", "accelerator", "memory", "property", "free", "fast"], 5 | "license": "MIT", 6 | "minimum-stability": "stable", 7 | "authors": [ 8 | { 9 | "name": "Keyvan Akbary", 10 | "email": "keyvan@mybuilder.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=7.1", 15 | "phpunit/phpunit": ">=7.0 <8" 16 | }, 17 | "autoload": { 18 | "psr-4": { "MyBuilder\\PhpunitAccelerator\\": "src" } 19 | }, 20 | "config": { 21 | "bin-dir": "bin" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 18 | ./tests 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/IgnoreTestPolicy.php: -------------------------------------------------------------------------------- 1 | ignorePolicy = ($ignorePolicy) ?: new NeverIgnoreTestPolicy(); 19 | } 20 | 21 | public function endTest(\PHPUnit\Framework\Test $test, float $time): void 22 | { 23 | $testReflection = new \ReflectionObject($test); 24 | 25 | if ($this->ignorePolicy->shouldIgnore($testReflection)) { 26 | return; 27 | } 28 | 29 | $this->safelyFreeProperties($test, $testReflection->getProperties()); 30 | } 31 | 32 | private function safelyFreeProperties(\PHPUnit\Framework\Test $test, array $properties) 33 | { 34 | foreach ($properties as $property) { 35 | if ($this->isSafeToFreeProperty($property)) { 36 | $this->freeProperty($test, $property); 37 | } 38 | } 39 | } 40 | 41 | private function isSafeToFreeProperty(\ReflectionProperty $property) 42 | { 43 | return !$property->isStatic() && $this->isNotPhpUnitProperty($property); 44 | } 45 | 46 | private function isNotPhpUnitProperty(\ReflectionProperty $property) 47 | { 48 | return 0 !== strpos($property->getDeclaringClass()->getName(), self::PHPUNIT_PROPERTY_PREFIX); 49 | } 50 | 51 | private function freeProperty(\PHPUnit\Framework\Test $test, \ReflectionProperty $property) 52 | { 53 | $property->setAccessible(true); 54 | $property->setValue($test, null); 55 | } 56 | } 57 | 58 | class NeverIgnoreTestPolicy implements IgnoreTestPolicy 59 | { 60 | public function shouldIgnore(\ReflectionObject $testReflection) 61 | { 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/TestListenerTest.php: -------------------------------------------------------------------------------- 1 | dummyTest = new DummyTest(); 13 | } 14 | 15 | /** 16 | * @test 17 | */ 18 | public function shouldFreeTestProperty() 19 | { 20 | $this->endTest(new TestListener()); 21 | 22 | $this->assertFreesTestProperty(); 23 | } 24 | 25 | private function endTest(TestListener $listener) 26 | { 27 | $listener->endTest($this->dummyTest, 0); 28 | } 29 | 30 | private function assertFreesTestProperty() 31 | { 32 | $this->assertNull($this->dummyTest->property); 33 | } 34 | 35 | /** 36 | * @test 37 | */ 38 | public function shouldNotFreePhpUnitProperty() 39 | { 40 | $this->endTest(new TestListener()); 41 | 42 | $this->assertDoesNotFreePHPUnitProperty(); 43 | } 44 | 45 | private function assertDoesNotFreePHPUnitProperty() 46 | { 47 | $this->assertNotNull($this->dummyTest->phpUnitProperty); 48 | } 49 | 50 | /** 51 | * @test 52 | */ 53 | public function shouldNotFreeTestPropertyWithIgnoreAlwaysPolicy() 54 | { 55 | $this->endTest(new TestListener(new AlwaysIgnoreTestPolicy())); 56 | 57 | $this->assertDoesNotFreeTestProperty(); 58 | } 59 | 60 | private function assertDoesNotFreeTestProperty() 61 | { 62 | $this->assertNotNull($this->dummyTest->property); 63 | } 64 | } 65 | 66 | class PHPUnit_Fake extends \PHPUnit\Framework\TestCase 67 | { 68 | public $phpUnitProperty = 1; 69 | } 70 | 71 | class DummyTest extends \PHPUnit_Fake 72 | { 73 | public $property = 1; 74 | } 75 | 76 | class AlwaysIgnoreTestPolicy implements IgnoreTestPolicy 77 | { 78 | public function shouldIgnore(\ReflectionObject $testReflection) 79 | { 80 | return true; 81 | } 82 | } 83 | --------------------------------------------------------------------------------