├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── README.md ├── composer.json ├── phpstan.neon ├── phpunit.xml.dist ├── src ├── AccessibilityHelperTrait.php └── CompareTrait.php └── tests ├── TestCase ├── AccessibilityHelperTraitTest.php ├── CompareTraitTest.php ├── html │ ├── attributes.html │ ├── empty.html │ ├── nested.html │ └── simple.html ├── json │ ├── empty.json │ ├── simple-array.json │ ├── simple-dict.json │ └── simple-int.json └── xml │ ├── empty.xml │ └── simple.xml └── comparisons └── CompareTrait ├── attributes.html ├── empty.html ├── empty.json ├── empty.xml ├── nested.html ├── simple-array.json ├── simple-dict.json ├── simple-int.json ├── simple.html └── simple.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig 2 | # http://editorconfig.org/ 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.yml] 14 | indent_size = 2 15 | 16 | [*.neon] 17 | indent_style = tab 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | testsuite: 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | php-version: ['8.2'] 18 | prefer-lowest: [''] 19 | include: 20 | - php-version: '8.1' 21 | prefer-lowest: 'prefer-lowest' 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Setup PHP 27 | uses: shivammathur/setup-php@v2 28 | with: 29 | php-version: ${{ matrix.php-version }} 30 | extensions: mbstring, intl 31 | coverage: none 32 | 33 | - name: Composer install 34 | run: | 35 | if ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then 36 | composer update --prefer-lowest --prefer-stable 37 | else 38 | composer update 39 | fi 40 | 41 | - name: Run PHPUnit 42 | run: vendor/bin/phpunit 43 | 44 | cs-stan: 45 | name: Coding Standard & Static Analysis 46 | runs-on: ubuntu-22.04 47 | 48 | steps: 49 | - uses: actions/checkout@v4 50 | 51 | - name: Setup PHP 52 | uses: shivammathur/setup-php@v2 53 | with: 54 | php-version: '8.1' 55 | extensions: mbstring, intl 56 | coverage: none 57 | tools: phpstan:1.10,cs2pr 58 | 59 | - name: Composer Install 60 | run: composer require cakephp/cakephp-codesniffer 61 | 62 | - name: Run phpcs 63 | run: vendor/bin/phpcs --report=checkstyle --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/ | cs2pr 64 | 65 | - name: Run phpstan 66 | if: always() 67 | run: phpstan --error-format=github 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | phpunit.xml 3 | vendor 4 | .phpunit.cache 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Test utilities 2 | 3 | This package contains support traits to ease unit testing. 4 | 5 | ## Installing via composer 6 | 7 | You should install this package into your project using composer. To do so you 8 | can run the following command: 9 | 10 | ```bash 11 | composer require friendsofcake/cakephp-test-utilities 12 | ``` 13 | 14 | ## Traits 15 | 16 | At this point there are two traits: 17 | 18 | 1. [`AccessibilityHelperTrait`](#accessibilityhelpertrait) : Gain access protected properties and methods. 19 | 2. [`CompareTrait`](#comparetrait) : Assert methods, comparing to files for: HTML, JSON, XML 20 | 21 | ### AccessibilityHelperTrait 22 | 23 | This trait gains you access to protected properties and methods. You don't need 24 | of a new class with pass-through methods. It uses reflection to achieve this. 25 | 26 | #### Setup 27 | 28 | Add the trait at the top of your test case: 29 | 30 | ``` php 31 | use \FriendsOfCake\TestUtilities\AccessibilityHelperTrait; 32 | ``` 33 | 34 | Now that you have the trait you need to set which object you want to access. 35 | You can do this globally for the entire test in `setUp()` or in your test 36 | methods: 37 | 38 | ``` php 39 | $object = new ObjectIAmGoingToTest(); 40 | $this->setReflectionClassInstance($object); 41 | $this->defaultReflectionTarget = $object; // (optional) 42 | ``` 43 | 44 | #### Protected properties 45 | 46 | You can get and set the protected properties: 47 | 48 | ``` php 49 | $data = 'FriendsOfCake'; 50 | $this->setProtectedProperty('_myProperty', $data, $object); 51 | 52 | $expected = $data; 53 | $actual = $this->getProtectedProperty('_myProperty', $object); 54 | $this->assertEquals($expected, $actual); 55 | ``` 56 | 57 | #### Protected methods 58 | 59 | You can directly call protected methods: 60 | 61 | ``` php 62 | $parameters = [$argument1, $argument2]; 63 | 64 | $expected = $expectedReturnValue; 65 | $actual = $this->callProtectedMethod('_myMethod', $parameters, $object); 66 | $this->assertEquals($expected, $actual); 67 | ``` 68 | 69 | ### CompareTrait 70 | 71 | This trait helps with comparing test results as string 72 | 73 | #### Setup 74 | 75 | Add the trait at the top of your test case and define the `_compareBasePath` 76 | property so the trait knows where to look for comparison files: 77 | 78 | ``` php 79 | ... 80 | use \FriendsOfCake\TestUtilities\CompareTrait; 81 | 82 | class MyTest extends TestCase 83 | { 84 | use CompareTrait; 85 | 86 | public function setUp(): void 87 | { 88 | parent::setUp(); 89 | 90 | $this->_compareBasePath = 'comparisons/MyTest/'; 91 | } 92 | } 93 | ``` 94 | 95 | #### Usage 96 | 97 | Each of the methods acts similar to the core `assertSameAsFile` method: 98 | 99 | ```php 100 | public function testExample() 101 | { 102 | $html = '

Some html

'; 103 | $xml = '...'; 104 | $json = ['actually' => 'this is an array']; 105 | 106 | $this->assertHtmlSameAsFile('some.html', $html); 107 | $this->assertXmlSameAsFile('some.xml', $xml); 108 | $this->assertJsonSameAsFile('some.json', $json); 109 | } 110 | ``` 111 | 112 | See [Cake's docs](https://book.cakephp.org/5/en/development/testing.html#comparing-test-results-to-a-file) 113 | for more details on usage of `assertSameAsFile` on which these methods are 114 | based. 115 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friendsofcake/cakephp-test-utilities", 3 | "description": "Package with support traits to ease unit testing.", 4 | "keywords": ["test", "unit testing", "unittesting", "phpunit"], 5 | "homepage": "https://github.com/friendsofcake/cakephp-test-utilities", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Christian Winther", 10 | "role": "Author" 11 | }, 12 | { 13 | "name": "Andy Dawson", 14 | "role": "Author" 15 | }, 16 | { 17 | "name": "Frank de Graaf", 18 | "role": "Polisher" 19 | } 20 | ], 21 | "require": { 22 | "cakephp/cakephp": "^5.0" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "^10.1" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "FriendsOfCake\\TestUtilities\\": "src", 30 | "FriendsOfCake\\TestUtilities\\Test\\": "tests" 31 | } 32 | }, 33 | "config": { 34 | "sort-packages": true, 35 | "allow-plugins": { 36 | "dealerdirect/phpcodesniffer-composer-installer": true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 7 3 | paths: 4 | - src/ 5 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./tests/ 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/AccessibilityHelperTrait.php: -------------------------------------------------------------------------------- 1 | instances used for invocation. 41 | * 42 | * @var array 43 | */ 44 | protected array $_reflectionInstanceCache = []; 45 | 46 | /** 47 | * Reset the internal reflection caches. 48 | * 49 | * @return void 50 | */ 51 | public function resetReflectionCache(): void 52 | { 53 | $this->_reflectionPropertyCache = []; 54 | $this->_reflectionMethodCache = []; 55 | $this->_reflectionInstanceCache = []; 56 | } 57 | 58 | /** 59 | * Map an instance of an object to its class name. 60 | * 61 | * @param object $instance an instance of a class 62 | * @param string|null $class the key used in the reflection instance class 63 | * @return void 64 | */ 65 | public function setReflectionClassInstance(object $instance, ?string $class = null): void 66 | { 67 | $class = $class ?: get_class($instance); 68 | $this->_reflectionInstanceCache[$class] = $instance; 69 | } 70 | 71 | /** 72 | * Get working instance of "$class". 73 | * 74 | * @param string|null $class the class name 75 | * @return object 76 | * @throws \Exception 77 | */ 78 | public function getReflectionInstance(?string $class = null): object 79 | { 80 | $class = $this->_getReflectionTargetClass($class); 81 | if (empty($this->_reflectionInstanceCache[$class])) { 82 | throw new Exception(sprintf( 83 | 'Unable to find instance of %s in the reflection cache. ' 84 | . 'Have you added it using "setReflectionClassInstance"?', 85 | $class 86 | )); 87 | } 88 | 89 | return $this->_reflectionInstanceCache[$class]; 90 | } 91 | 92 | /** 93 | * Helper method to call a protected method. 94 | * 95 | * @param string $method the method name 96 | * @param array $args Argument list to call $method with (call_user_func_array style) 97 | * @param object|string|null $class Target reflection class 98 | * @return mixed 99 | */ 100 | public function callProtectedMethod(string $method, array $args = [], object|string|null $class = null): mixed 101 | { 102 | $class = $this->_getReflectionTargetClass($class); 103 | $cacheKey = $class . '_' . $method; 104 | 105 | if (!isset($this->_reflectionMethodCache[$cacheKey])) { 106 | $this->_reflectionMethodCache[$cacheKey] = $this->_getNewReflectionMethod($class, $method); 107 | $this->_reflectionMethodCache[$cacheKey]->setAccessible(true); 108 | } 109 | 110 | return $this->_reflectionMethodCache[$cacheKey]->invokeArgs($this->getReflectionInstance($class), $args); 111 | } 112 | 113 | /** 114 | * Helper method to get the value of a protected property. 115 | * 116 | * @param string $property the property to access/manipulate 117 | * @param string $class Target reflection class 118 | * @return mixed 119 | */ 120 | public function getProtectedProperty(string $property, ?string $class = null): mixed 121 | { 122 | $Instance = $this->_getReflectionPropertyInstance($property, $class); 123 | 124 | return $Instance->getValue($this->getReflectionInstance($class)); 125 | } 126 | 127 | /** 128 | * Helper method to set the value of a protected property. 129 | * 130 | * @param string $property the property to change 131 | * @param mixed $value the value to set the property to 132 | * @param string $class Target reflection class 133 | * @return void 134 | */ 135 | public function setProtectedProperty(string $property, mixed $value, ?string $class = null): void 136 | { 137 | $this->_getReflectionPropertyInstance($property, $class) 138 | ->setValue($this->getReflectionInstance($class), $value); 139 | } 140 | 141 | /** 142 | * Get a reflection property object. 143 | * 144 | * @param string $property the property to access/manipulate 145 | * @param string|null $class the class name 146 | * @return \ReflectionProperty 147 | */ 148 | protected function _getReflectionPropertyInstance(string $property, ?string $class = null): ReflectionProperty 149 | { 150 | $class = $this->_getReflectionTargetClass($class); 151 | $cacheKey = $class . '_' . $property; 152 | 153 | if (!isset($this->_reflectionPropertyCache[$cacheKey])) { 154 | $this->_reflectionPropertyCache[$cacheKey] = $this->_getNewReflectionProperty($class, $property); 155 | $this->_reflectionPropertyCache[$cacheKey]->setAccessible(true); 156 | } 157 | 158 | return $this->_reflectionPropertyCache[$cacheKey]; 159 | } 160 | 161 | /** 162 | * Get the reflection class name. 163 | * 164 | * @param object|string|null $class the class name 165 | * @return string 166 | * @throws \Exception 167 | */ 168 | protected function _getReflectionTargetClass(object|string|null $class = null): string 169 | { 170 | if ($class === null) { 171 | if (!isset($this->defaultReflectionTarget)) { 172 | throw new Exception( 173 | 'Unable to find reflection target. ' 174 | . 'Have you set $defaultReflectionTarget or passed in a class name?' 175 | ); 176 | } 177 | 178 | $class = $this->defaultReflectionTarget; 179 | } 180 | 181 | if (is_string($class)) { 182 | return $class; 183 | } 184 | 185 | return get_class($class); 186 | } 187 | 188 | /** 189 | * Gets a new ReflectionMethod instance. Extracted for testing purposes. 190 | * 191 | * @param string $class the class name 192 | * @param string $method the method name 193 | * @return \ReflectionMethod 194 | */ 195 | protected function _getNewReflectionMethod(string $class, string $method): ReflectionMethod 196 | { 197 | return new ReflectionMethod($class, $method); 198 | } 199 | 200 | /** 201 | * Gets a new ReflectionProperty instance. Extracted for testing purposes. 202 | * 203 | * @param string $class the class name 204 | * @param string $property the property to access/manipulate 205 | * @return \ReflectionProperty 206 | */ 207 | protected function _getNewReflectionProperty(string $class, string $property): ReflectionProperty 208 | { 209 | return new ReflectionProperty($class, $property); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/CompareTrait.php: -------------------------------------------------------------------------------- 1 | indentHtml($result); 32 | $this->assertSameAsFile($path, $indented); 33 | } 34 | 35 | /** 36 | * Assert json is the same as a file 37 | * 38 | * Compares the array representation 39 | * 40 | * @param string $path partial path to test comparison file 41 | * @param mixed $result test result as an array 42 | * @return void 43 | */ 44 | public function assertJsonSameAsFile(string $path, mixed $result): void 45 | { 46 | if (!file_exists($path)) { 47 | $path = $this->_compareBasePath . $path; 48 | 49 | $dir = dirname($path); 50 | if (!is_dir($dir)) { 51 | mkdir($dir, 0777, true); 52 | } 53 | } 54 | $this->_updateComparisons ??= getenv('UPDATE_TEST_COMPARISON_FILES') ?: false; 55 | if ($this->_updateComparisons) { 56 | $indented = json_encode( 57 | $result, 58 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE 59 | ) . "\n"; 60 | file_put_contents($path, $indented); 61 | } 62 | 63 | $expected = json_decode(file_get_contents($path), true); 64 | $this->assertEquals($expected, $result); 65 | } 66 | 67 | /** 68 | * Asert xml is the same as a comparison file 69 | * 70 | * @param string $path partial path to test comparison file 71 | * @param string $result test result as a string 72 | * @return void 73 | */ 74 | public function assertXmlSameAsFile(string $path, string $result): void 75 | { 76 | $indented = $this->indentXml($result); 77 | $this->assertSameAsFile($path, $indented); 78 | } 79 | 80 | /** 81 | * If compare base path has not been set, use the test file as the base 82 | * 83 | * @return void 84 | */ 85 | protected function initComparePath(): void 86 | { 87 | if ($this->_compareBasePath) { 88 | return; 89 | } 90 | 91 | $reflector = new ReflectionClass($this); 92 | $this->_compareBasePath = str_replace( 93 | 'TestCase', 94 | 'comparisons', 95 | substr($reflector->getFileName(), 0, -8) 96 | ) . DIRECTORY_SEPARATOR; 97 | } 98 | 99 | /** 100 | * Indent html for consistent whitespace and indentation 101 | * 102 | * Start from everything on one line 103 | * Indent tags 104 | * Indent atttributes one level more than the tag 105 | * 106 | * @param string $html the html string 107 | * @return string 108 | */ 109 | protected function indentHtml(string $html): string 110 | { 111 | $html = trim(preg_replace("/\s+/", ' ', $html)); 112 | 113 | $counter = 0; 114 | $callback = function ($match) use (&$counter) { 115 | $isTag = $match[1][0] === '<'; 116 | 117 | $indent = str_repeat(' ', $counter); 118 | 119 | if ($isTag) { 120 | $match[1] = preg_replace( 121 | '@ ([\w-]+="[^"]*")@', 122 | "\n $indent\\1", 123 | $match[1] 124 | ); 125 | $isClosingTag = (bool)$match[2]; 126 | $isSelfClosingTag = (bool)$match[3]; 127 | 128 | if ($isClosingTag) { 129 | $counter--; 130 | $indent = str_repeat(' ', $counter); 131 | } elseif (!$isSelfClosingTag) { 132 | $counter++; 133 | } 134 | } 135 | 136 | return $indent . rtrim($match[1]) . "\n"; 137 | }; 138 | 139 | return ltrim(preg_replace_callback('@(<(/?)[^>]+?(/?)>|[^<]+)(?:\s*)@', $callback, $html)); 140 | } 141 | 142 | /** 143 | * Use exactly the same routine as for html 144 | * 145 | * However stash the xml header so there isn't an extra level of unwanted 146 | * indentation 147 | * 148 | * @param string $xml the xml string 149 | * @return string 150 | */ 151 | protected function indentXml(string $xml): string 152 | { 153 | $header = ''; 154 | $headerPos = strpos($xml, '?>'); 155 | if ($headerPos) { 156 | $headerPos += 2; 157 | $header = trim(substr($xml, 0, $headerPos)) . "\n"; 158 | $xml = trim(substr($xml, $headerPos)); 159 | } 160 | 161 | return $header . $this->indentHtml($xml); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /tests/TestCase/AccessibilityHelperTraitTest.php: -------------------------------------------------------------------------------- 1 | _trait = $this->getMockForTrait(self::TRAIT_NAME); 34 | $this->setReflectionClassInstance($this->_trait); 35 | $this->defaultReflectionTarget = $this->_trait; 36 | } 37 | 38 | /** 39 | * Tests AccessibilityHelperTrait::resetReflectionCache(). 40 | */ 41 | public function testResetReflectionCache() 42 | { 43 | $this->setProtectedProperty('_reflectionPropertyCache', ['_reflectionPropertyCache']); 44 | $this->setProtectedProperty('_reflectionMethodCache', ['_reflectionMethodCache']); 45 | $this->setProtectedProperty('_reflectionInstanceCache', ['_reflectionInstanceCache']); 46 | 47 | $this->assertNotEmpty($this->getProtectedProperty('_reflectionPropertyCache')); 48 | $this->assertNotEmpty($this->getProtectedProperty('_reflectionMethodCache')); 49 | $this->assertNotEmpty($this->getProtectedProperty('_reflectionInstanceCache')); 50 | 51 | $this->_trait->resetReflectionCache(); 52 | 53 | $this->assertEmpty($this->getProtectedProperty('_reflectionPropertyCache')); 54 | $this->assertEmpty($this->getProtectedProperty('_reflectionMethodCache')); 55 | $this->assertEmpty($this->getProtectedProperty('_reflectionInstanceCache')); 56 | } 57 | 58 | /** 59 | * Tests AccessibilityHelperTrait::setReflectionClassInstance(). 60 | */ 61 | public function testSetReflectionClassInstance() 62 | { 63 | $this->_trait->setReflectionClassInstance($this); 64 | $this->_trait->setReflectionClassInstance($this, 'MyTestInstance'); 65 | $this->_trait->setReflectionClassInstance($this->_trait, 'MyTestInstance'); 66 | 67 | $expected = [ 68 | static::class => $this, 69 | 'MyTestInstance' => $this->_trait, 70 | ]; 71 | $actual = $this->getProtectedProperty('_reflectionInstanceCache'); 72 | $this->assertSame($expected, $actual); 73 | } 74 | 75 | /** 76 | * Tests AccessibilityHelperTrait::setReflectionClassInstance(). 77 | * 78 | * Get existing objects from the cache. 79 | */ 80 | public function testGetReflectionInstanceExisting() 81 | { 82 | $this->_trait->setReflectionClassInstance($this, 'MyTestInstance'); 83 | 84 | $expected = $this; 85 | $actual = $this->_trait->getReflectionInstance('MyTestInstance'); 86 | $this->assertSame($expected, $actual); 87 | } 88 | 89 | /** 90 | * Tests AccessibilityHelperTrait::setReflectionClassInstance(). 91 | * 92 | * Get missing objects from the cache. 93 | */ 94 | public function testGetReflectionInstanceMissing() 95 | { 96 | $this->expectException(Exception::class); 97 | 98 | $this->_trait->getReflectionInstance('MyTestInstance'); 99 | } 100 | 101 | /** 102 | * Tests AccessibilityHelperTrait::callProtectedMethod(). 103 | * 104 | * Call a new protected method. One that hasn't been called before. 105 | */ 106 | public function testCallProtectedMethodNewInstance() 107 | { 108 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 109 | '_getReflectionTargetClass', '_getNewReflectionMethod', 'getReflectionInstance', 110 | ]); 111 | $this->setReflectionClassInstance($this->_trait); 112 | $this->defaultReflectionTarget = $this->_trait; 113 | 114 | $method = $this->getMockBuilder('\ReflectionMethod') 115 | ->disableOriginalConstructor() 116 | ->getMock(); 117 | 118 | $this->_trait->expects($this->once()) 119 | ->method('_getReflectionTargetClass') 120 | ->with('FOC') 121 | ->will($this->returnValue('FocClass')); 122 | 123 | $this->_trait->expects($this->once()) 124 | ->method('_getNewReflectionMethod') 125 | ->with('FocClass', 'getFOC') 126 | ->will($this->returnValue($method)); 127 | 128 | $method->expects($this->once()) 129 | ->method('setAccessible') 130 | ->with(true); 131 | 132 | $this->_trait->expects($this->once()) 133 | ->method('getReflectionInstance') 134 | ->with('FocClass') 135 | ->will($this->returnValue($this->_trait)); 136 | 137 | $method->expects($this->once()) 138 | ->method('invokeArgs') 139 | ->with($this->_trait, [42, true]) 140 | ->will($this->returnValue('FriendsOfCake')); 141 | 142 | $expected = 'FriendsOfCake'; 143 | $actual = $this->_trait->callProtectedMethod('getFOC', [42, true], 'FOC'); 144 | $this->assertEquals($expected, $actual); 145 | 146 | $expected = ['FocClass_getFOC' => $method]; 147 | $actual = $this->getProtectedProperty('_reflectionMethodCache'); 148 | $this->assertSame($expected, $actual); 149 | } 150 | 151 | /** 152 | * Tests AccessibilityHelperTrait::callProtectedMethod(). 153 | * 154 | * Call an existing protected method. One that has been called before. 155 | */ 156 | public function testCallProtectedMethodExistingInstance() 157 | { 158 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 159 | '_getReflectionTargetClass', '_getNewReflectionMethod', 'getReflectionInstance', 160 | ]); 161 | $this->setReflectionClassInstance($this->_trait); 162 | $this->defaultReflectionTarget = $this->_trait; 163 | 164 | $method = $this->getMockBuilder('\ReflectionMethod') 165 | ->disableOriginalConstructor() 166 | ->getMock(); 167 | $cache = ['FocClass_getFOC' => $method]; 168 | $this->setProtectedProperty('_reflectionMethodCache', $cache); 169 | 170 | $this->_trait->expects($this->once()) 171 | ->method('_getReflectionTargetClass') 172 | ->with('FOC') 173 | ->will($this->returnValue('FocClass')); 174 | 175 | $this->_trait->expects($this->never()) 176 | ->method('_getNewReflectionMethod'); 177 | 178 | $method->expects($this->never()) 179 | ->method('setAccessible'); 180 | 181 | $this->_trait->expects($this->once()) 182 | ->method('getReflectionInstance') 183 | ->with('FocClass') 184 | ->will($this->returnValue($this->_trait)); 185 | 186 | $method->expects($this->once()) 187 | ->method('invokeArgs') 188 | ->with($this->_trait, [42, true]) 189 | ->will($this->returnValue('FriendsOfCake')); 190 | 191 | $expected = 'FriendsOfCake'; 192 | $actual = $this->_trait->callProtectedMethod('getFOC', [42, true], 'FOC'); 193 | $this->assertEquals($expected, $actual); 194 | 195 | $expected = $cache; 196 | $actual = $this->getProtectedProperty('_reflectionMethodCache'); 197 | $this->assertSame($expected, $actual); 198 | } 199 | 200 | /** 201 | * Tests AccessibilityHelperTrait::getProtectedProperty(). 202 | */ 203 | public function testGetProtectedProperty() 204 | { 205 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 206 | '_getReflectionPropertyInstance', 'getReflectionInstance', 207 | ]); 208 | 209 | $property = $this->getMockBuilder('\ReflectionProperty') 210 | ->disableOriginalConstructor() 211 | ->getMock(); 212 | 213 | $this->_trait->expects($this->once()) 214 | ->method('_getReflectionPropertyInstance') 215 | ->with('_myProperty', 'MyClass') 216 | ->will($this->returnValue($property)); 217 | 218 | $this->_trait->expects($this->once()) 219 | ->method('getReflectionInstance') 220 | ->with('MyClass') 221 | ->will($this->returnValue($this->_trait)); 222 | 223 | $property->expects($this->once()) 224 | ->method('getValue') 225 | ->with($this->_trait) 226 | ->will($this->returnValue('FriendsOfCake')); 227 | 228 | $expected = 'FriendsOfCake'; 229 | $actual = $this->_trait->getProtectedProperty('_myProperty', 'MyClass'); 230 | $this->assertEquals($expected, $actual); 231 | } 232 | 233 | /** 234 | * Tests AccessibilityHelperTrait::setProtectedProperty(). 235 | */ 236 | public function testSetProtectedProperty() 237 | { 238 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 239 | '_getReflectionPropertyInstance', 'getReflectionInstance', 240 | ]); 241 | 242 | $property = $this->getMockBuilder('\ReflectionProperty') 243 | ->disableOriginalConstructor() 244 | ->getMock(); 245 | 246 | $this->_trait->expects($this->once()) 247 | ->method('_getReflectionPropertyInstance') 248 | ->with('_myProperty', 'MyClass') 249 | ->will($this->returnValue($property)); 250 | 251 | $this->_trait->expects($this->once()) 252 | ->method('getReflectionInstance') 253 | ->with('MyClass') 254 | ->will($this->returnValue($this->_trait)); 255 | 256 | $property->expects($this->once()) 257 | ->method('setValue') 258 | ->with($this->_trait, 'FriendsOfCake') 259 | ->will($this->returnValue('FriendsOfCake')); 260 | 261 | $this->_trait->setProtectedProperty('_myProperty', 'FriendsOfCake', 'MyClass'); 262 | } 263 | 264 | /** 265 | * Tests AccessibilityHelperTrait::_getReflectionPropertyInstance(). 266 | * 267 | * Without any previously stored properties. 268 | */ 269 | public function testProtectedGetReflectionPropertyInstanceWithoutCache() 270 | { 271 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 272 | '_getReflectionTargetClass', '_getNewReflectionProperty', 273 | ]); 274 | $this->setReflectionClassInstance($this->_trait); 275 | $this->defaultReflectionTarget = $this->_trait; 276 | 277 | $property = $this->getMockBuilder('\ReflectionProperty') 278 | ->disableOriginalConstructor() 279 | ->getMock(); 280 | 281 | $this->_trait->expects($this->once()) 282 | ->method('_getReflectionTargetClass') 283 | ->with('FocClass') 284 | ->will($this->returnValue('FocTargetClass')); 285 | 286 | $this->_trait->expects($this->once()) 287 | ->method('_getNewReflectionProperty') 288 | ->with('FocTargetClass', '_focValue') 289 | ->will($this->returnValue($property)); 290 | 291 | $property->expects($this->once()) 292 | ->method('setAccessible') 293 | ->with(true); 294 | 295 | $expected = $property; 296 | $actual = $this->callProtectedMethod('_getReflectionPropertyInstance', ['_focValue', 'FocClass']); 297 | $this->assertEquals($expected, $actual); 298 | 299 | $expected = ['FocTargetClass__focValue' => $property]; 300 | $actual = $this->getProtectedProperty('_reflectionPropertyCache'); 301 | $this->assertSame($expected, $actual); 302 | } 303 | 304 | /** 305 | * Tests AccessibilityHelperTrait::_getReflectionPropertyInstance(). 306 | * 307 | * With a previously stored property. 308 | */ 309 | public function testProtectedGetReflectionPropertyInstanceWithCache() 310 | { 311 | $this->_trait = $this->getMockForTrait(self::TRAIT_NAME, [], '', true, true, true, [ 312 | '_getReflectionTargetClass', '_getNewReflectionProperty', 313 | ]); 314 | $this->setReflectionClassInstance($this->_trait); 315 | $this->defaultReflectionTarget = $this->_trait; 316 | 317 | $property = $this->getMockBuilder('\ReflectionProperty') 318 | ->disableOriginalConstructor() 319 | ->getMock(); 320 | $cache = ['FocTargetClass__focValue' => $property]; 321 | 322 | $this->setProtectedProperty('_reflectionPropertyCache', $cache); 323 | 324 | $this->_trait->expects($this->once()) 325 | ->method('_getReflectionTargetClass') 326 | ->with('FocClass') 327 | ->will($this->returnValue('FocTargetClass')); 328 | 329 | $this->_trait->expects($this->never()) 330 | ->method('_getNewReflectionProperty'); 331 | 332 | $expected = $property; 333 | $actual = $this->callProtectedMethod('_getReflectionPropertyInstance', ['_focValue', 'FocClass']); 334 | $this->assertEquals($expected, $actual); 335 | 336 | $expected = $cache; 337 | $actual = $this->getProtectedProperty('_reflectionPropertyCache'); 338 | $this->assertSame($expected, $actual); 339 | } 340 | 341 | /** 342 | * Tests AccessibilityHelperTrait::_getReflectionTargetClass(). 343 | * 344 | * With valid values. 345 | */ 346 | public function testProtectedGetReflectionTargetClassValidValues() 347 | { 348 | $expected = 'MyClass'; 349 | $actual = $this->callProtectedMethod('_getReflectionTargetClass', ['MyClass']); 350 | $this->assertSame($expected, $actual); 351 | 352 | $expected = static::class; 353 | $actual = $this->callProtectedMethod('_getReflectionTargetClass', [$this]); 354 | $this->assertSame($expected, $actual); 355 | 356 | $this->_trait->defaultReflectionTarget = 'MyClass'; 357 | 358 | $expected = 'MyClass'; 359 | $actual = $this->callProtectedMethod('_getReflectionTargetClass', [null]); 360 | $this->assertSame($expected, $actual); 361 | 362 | $this->_trait->defaultReflectionTarget = $this; 363 | 364 | $expected = static::class; 365 | $actual = $this->callProtectedMethod('_getReflectionTargetClass', [null]); 366 | $this->assertSame($expected, $actual); 367 | 368 | $expected = 'MyClass'; 369 | $actual = $this->callProtectedMethod('_getReflectionTargetClass', ['MyClass']); 370 | $this->assertSame($expected, $actual); 371 | } 372 | 373 | /** 374 | * Tests AccessibilityHelperTrait::_getReflectionTargetClass(). 375 | * 376 | * With invalid values to trigger the exception. 377 | */ 378 | public function testProtectedGetReflectionTargetClassInvalidValues() 379 | { 380 | $this->expectException(Exception::class); 381 | 382 | $this->callProtectedMethod('_getReflectionTargetClass', [null]); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /tests/TestCase/CompareTraitTest.php: -------------------------------------------------------------------------------- 1 | initComparePath(); 18 | } 19 | 20 | /** 21 | * testAssertHtmlSameAsFile 22 | * 23 | * @dataProvider htmlInputFilesProvider 24 | * @param mixed $name 25 | */ 26 | public function testAssertHtmlSameAsFile($name) 27 | { 28 | $input = file_get_contents($name); 29 | $this->assertHtmlSameAsFile(basename($name), $input); 30 | } 31 | 32 | /** 33 | * testAssertJsonSameAsFile 34 | * 35 | * @dataProvider jsonInputFilesProvider 36 | * @param mixed $name 37 | */ 38 | public function testAssertJsonSameAsFile($name) 39 | { 40 | $input = json_decode(file_get_contents($name), true); 41 | $this->assertJsonSameAsFile(basename($name), $input); 42 | } 43 | 44 | /** 45 | * testAssertXmlSameAsFile 46 | * 47 | * @dataProvider xmlInputFilesProvider 48 | * @param mixed $name 49 | */ 50 | public function testAssertXmlSameAsFile($name) 51 | { 52 | $input = file_get_contents($name); 53 | $this->assertXmlSameAsFile(basename($name), $input); 54 | } 55 | 56 | public static function htmlInputFilesProvider() 57 | { 58 | return self::findFiles('html'); 59 | } 60 | 61 | public static function jsonInputFilesProvider() 62 | { 63 | return self::findFiles('json'); 64 | } 65 | 66 | public static function xmlInputFilesProvider() 67 | { 68 | return self::findFiles('xml'); 69 | } 70 | 71 | protected static function findFiles($format) 72 | { 73 | // phpcs:ignore 74 | $reflector = new ReflectionClass(__CLASS__); 75 | $path = dirname($reflector->getFileName()) . '/' . $format . '/'; 76 | 77 | $return = []; 78 | foreach (glob("{$path}*.$format") as $file) { 79 | $return[str_replace($path, '', $file)] = [$file]; 80 | } 81 | 82 | return $return; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/TestCase/html/attributes.html: -------------------------------------------------------------------------------- 1 |

Stuff

2 | -------------------------------------------------------------------------------- /tests/TestCase/html/empty.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfCake/cakephp-test-utilities/d6f2fd7a3a4b44d6db5e44353142db995c62661d/tests/TestCase/html/empty.html -------------------------------------------------------------------------------- /tests/TestCase/html/nested.html: -------------------------------------------------------------------------------- 1 |

Some indented Stuff

2 | -------------------------------------------------------------------------------- /tests/TestCase/html/simple.html: -------------------------------------------------------------------------------- 1 |

A paragraph.

2 | -------------------------------------------------------------------------------- /tests/TestCase/json/empty.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfCake/cakephp-test-utilities/d6f2fd7a3a4b44d6db5e44353142db995c62661d/tests/TestCase/json/empty.json -------------------------------------------------------------------------------- /tests/TestCase/json/simple-array.json: -------------------------------------------------------------------------------- 1 | ["one", "two"] 2 | -------------------------------------------------------------------------------- /tests/TestCase/json/simple-dict.json: -------------------------------------------------------------------------------- 1 | {"one": 1, "two": 2} 2 | -------------------------------------------------------------------------------- /tests/TestCase/json/simple-int.json: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/TestCase/xml/empty.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfCake/cakephp-test-utilities/d6f2fd7a3a4b44d6db5e44353142db995c62661d/tests/TestCase/xml/empty.xml -------------------------------------------------------------------------------- /tests/TestCase/xml/simple.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/attributes.html: -------------------------------------------------------------------------------- 1 |

6 | Stuff 7 |

8 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/empty.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfCake/cakephp-test-utilities/d6f2fd7a3a4b44d6db5e44353142db995c62661d/tests/comparisons/CompareTrait/empty.html -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/empty.json: -------------------------------------------------------------------------------- 1 | null 2 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/empty.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfCake/cakephp-test-utilities/d6f2fd7a3a4b44d6db5e44353142db995c62661d/tests/comparisons/CompareTrait/empty.xml -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/nested.html: -------------------------------------------------------------------------------- 1 |

2 | Some 3 | 4 | indented 5 | 6 | Stuff 7 |

8 | 16 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/simple-array.json: -------------------------------------------------------------------------------- 1 | [ 2 | "one", 3 | "two" 4 | ] 5 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/simple-dict.json: -------------------------------------------------------------------------------- 1 | { 2 | "one": 1, 3 | "two": 2 4 | } 5 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/simple-int.json: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/simple.html: -------------------------------------------------------------------------------- 1 |

2 | A paragraph. 3 |

4 | -------------------------------------------------------------------------------- /tests/comparisons/CompareTrait/simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------