├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── autoload └── phpunit-class-aliases.php ├── composer.json └── src ├── PHPUnit ├── Controller │ ├── AbstractConsoleControllerTestCase.php │ ├── AbstractControllerTestCase.php │ └── AbstractHttpControllerTestCase.php ├── TestCaseNoTypeHintTrait.php └── TestCaseTypeHintTrait.php └── Util └── ModuleLoader.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 3.3.1 - TBD 6 | 7 | ### Added 8 | 9 | - Nothing. 10 | 11 | ### Changed 12 | 13 | - Nothing. 14 | 15 | ### Deprecated 16 | 17 | - Nothing. 18 | 19 | ### Removed 20 | 21 | - Nothing. 22 | 23 | ### Fixed 24 | 25 | - Nothing. 26 | 27 | ## 3.3.0 - 2019-06-11 28 | 29 | ### Added 30 | 31 | - [#76](https://github.com/zendframework/zend-test/pull/76) adds support for PHPUnit 8 32 | 33 | Undesired PHPUnit update to ^8.0 can happen on PHP 7.2 and newer when relying 34 | on PHPUnit installation as an indirect dependency via zend-test. 35 | Please always declare direct dependency on `phpunit/phpunit` with suitable 36 | versions alongside with `zendframework/zend-test`. 37 | 38 | PHPUnit 8 incompatible test suite typically would error after the update 39 | with messages like "Fatal error: Declaration of *::setUp() must be 40 | compatible with *::setUp(): void" for any of the following methods: 41 | 42 | - `setUpBeforeClass()` 43 | - `tearDownAfterClass()` 44 | - `setUp()` 45 | - `tearDown()` 46 | 47 | Following command can be used to declare explicit dependency on older PHPUnit versions: 48 | ```bash 49 | composer require --dev phpunit/phpunit:"^7.5.12 || ^6.5.14 || ^5.7.14" --update-with-dependencies 50 | ``` 51 | 52 | ### Changed 53 | 54 | - Nothing. 55 | 56 | ### Deprecated 57 | 58 | - Nothing. 59 | 60 | ### Removed 61 | 62 | - Nothing. 63 | 64 | ### Fixed 65 | 66 | - Nothing. 67 | 68 | ## 3.2.2 - 2019-01-08 69 | 70 | ### Added 71 | 72 | - [#75](https://github.com/zendframework/zend-test/pull/75) adds support for PHP 7.3. 73 | 74 | ### Changed 75 | 76 | - Nothing. 77 | 78 | ### Deprecated 79 | 80 | - Nothing. 81 | 82 | ### Removed 83 | 84 | - Nothing. 85 | 86 | ### Fixed 87 | 88 | - [#74](https://github.com/zendframework/zend-test/pull/74) reverts changes introduced in version 3.2.1 to how superglobals are reset 89 | between tests, primarily by fixing the root problem -- base URL detection -- 90 | by requiring a zend-http version that fixes that detection. 91 | 92 | ## 3.2.1 - 2018-12-10 93 | 94 | ### Added 95 | 96 | - Nothing. 97 | 98 | ### Changed 99 | 100 | - Nothing. 101 | 102 | ### Deprecated 103 | 104 | - Nothing. 105 | 106 | ### Removed 107 | 108 | - Nothing. 109 | 110 | ### Fixed 111 | 112 | - [#70](https://github.com/zendframework/zend-test/pull/70) fixes a memory leak in controller test cases. 113 | 114 | - [#66](https://github.com/zendframework/zend-test/pull/66) Fixes globals not 115 | cleared for controller tests 116 | 117 | ## 3.2.0 - 2018-04-07 118 | 119 | ### Added 120 | 121 | - [#60](https://github.com/zendframework/zend-test/pull/60) Added support for 122 | PHPUnit 7 123 | - [#65](https://github.com/zendframework/zend-test/pull/65) Added support for 124 | query parameters in DELETE request in AbstractControllerTestCase 125 | 126 | ### Deprecated 127 | 128 | - Nothing. 129 | 130 | ### Removed 131 | 132 | - Nothing. 133 | 134 | ### Fixed 135 | 136 | - [#63](https://github.com/zendframework/zend-test/pull/63) Fixed compatibility 137 | with PHP 7.2 138 | 139 | ## 3.1.1 - 2017-10-29 140 | 141 | ### Added 142 | 143 | - Nothing. 144 | 145 | ### Deprecated 146 | 147 | - Nothing. 148 | 149 | ### Removed 150 | 151 | - Nothing. 152 | 153 | ### Fixed 154 | 155 | - [#55](https://github.com/zendframework/zend-test/pull/55) Fixes compatibility 156 | with PHPUnit 5.7.23 where empty expected exception message no longer means 157 | message is not checked. 158 | - [#49](https://github.com/zendframework/zend-test/pull/49) Fixes missing alias 159 | for compatibility with PHPUnit <6.0 160 | 161 | ## 3.1.0 - 2017-05-01 162 | 163 | ### Added 164 | 165 | - [#40](https://github.com/zendframework/zend-test/pull/40) and 166 | [#48](https://github.com/zendframework/zend-test/pull/48) add support for 167 | the PHPUnit 6 series, while retaining support for PHPUnit 4 and 5. 168 | 169 | ### Deprecated 170 | 171 | - Nothing. 172 | 173 | ### Removed 174 | 175 | - Nothing. 176 | 177 | ### Fixed 178 | 179 | - Nothing. 180 | 181 | ## 3.0.2 - 2016-09-06 182 | 183 | ### Added 184 | 185 | - Nothing. 186 | 187 | ### Deprecated 188 | 189 | - Nothing. 190 | 191 | ### Removed 192 | 193 | - Nothing. 194 | 195 | ### Fixed 196 | 197 | - [#33](https://github.com/zendframework/zend-test/pull/33) fixes 198 | `queryContentRegexAssertion()` (used by `assertQueryContentRegex()` and 199 | `assertXpathQueryContentRegex()`) properly checks all matching nodes for 200 | content matching the regular expression, instead of only the first. The 201 | prevents false negative assertions from occuring. 202 | - [#21](https://github.com/zendframework/zend-test/pull/21) updates the 203 | `sebastian/version` dependency to also allow v2.0 releases. 204 | - [#31](https://github.com/zendframework/zend-test/pull/31) fixes an issue with 205 | the `AbstractControllerTestCase` when used to test a console request. 206 | Previously, routes with multiple literal flags were never matched; they now 207 | are. 208 | 209 | ## 3.0.1 - 2016-06-15 210 | 211 | ### Added 212 | 213 | - Nothing. 214 | 215 | ### Deprecated 216 | 217 | - Nothing. 218 | 219 | ### Removed 220 | 221 | - Nothing. 222 | 223 | ### Fixed 224 | 225 | - [#26](https://github.com/zendframework/zend-test/pull/26) fixes how 226 | `$traceErrors` works under PHP 7 and PHPUnit 5. Any zend-test-specific 227 | assertion failures now append a list of all exception messages to the base 228 | message when the flag is enabled. 229 | 230 | ## 3.0.0 - 2016-05-31 231 | 232 | ### Added 233 | 234 | - This release adds support for zend-mvc v3. 235 | 236 | ### Deprecated 237 | 238 | - Nothing. 239 | 240 | ### Removed 241 | 242 | - This release removes support for PHP versions `< 5.6`. 243 | - This release removes support for zend-mvc v2. 244 | 245 | ### Fixed 246 | 247 | - Nothing. 248 | 249 | ## 2.6.2 - TBD 250 | 251 | ### Added 252 | 253 | - [#22](https://github.com/zendframework/zend-test/pull/22) adds and publishes 254 | the documentation to https://zendframework.github.io/zend-test/ 255 | 256 | ### Deprecated 257 | 258 | - Nothing. 259 | 260 | ### Removed 261 | 262 | - Nothing. 263 | 264 | ### Fixed 265 | 266 | - Nothing. 267 | 268 | ## 2.6.1 - 2016-03-02 269 | 270 | ### Added 271 | 272 | - Nothing. 273 | 274 | ### Deprecated 275 | 276 | - Nothing. 277 | 278 | ### Removed 279 | 280 | - Nothing. 281 | 282 | ### Fixed 283 | 284 | - [#20](https://github.com/zendframework/zend-test/pull/20) updates the zend-mvc 285 | requirement to 2.7.1, ensuring deprecation notices will not occur in the 286 | majority of circumstances. 287 | 288 | ## 2.6.0 - 2016-03-01 289 | 290 | ### Added 291 | 292 | - Nothing. 293 | 294 | ### Deprecated 295 | 296 | - Nothing. 297 | 298 | ### Removed 299 | 300 | - Nothing. 301 | 302 | ### Fixed 303 | 304 | - [#19](https://github.com/zendframework/zend-test/pull/19) updates the 305 | code to be forwards compatible with: 306 | - zend-eventmanager v3 307 | - zend-servicemanager v3 308 | - zend-stdlib v3 309 | - zend-mvc v2.7 310 | 311 | ## 2.5.3 - 2016-03-01 312 | 313 | ### Added 314 | 315 | - Nothing. 316 | 317 | ### Deprecated 318 | 319 | - Nothing. 320 | 321 | ### Removed 322 | 323 | - Nothing. 324 | 325 | ### Fixed 326 | 327 | - [#6](https://github.com/zendframework/zend-test/pull/6) updates the 328 | `AbstractControllerTestCase` to mark a test as failed if no route match occurs 329 | in a number of assertions that require a route match. 330 | - [#7](https://github.com/zendframework/zend-test/pull/7) modifies the `reset()` 331 | method of the `AbstractControllerTestCase` to prevent rewriting the 332 | `$_SESSION` superglobal if it has not previously been enabled. 333 | 334 | ## 2.5.2 - 2015-12-09 335 | 336 | ### Added 337 | 338 | - [#4](https://github.com/zendframework/zend-test/pull/4) PHPUnit v5 Support. 339 | 340 | ### Deprecated 341 | 342 | - Nothing. 343 | 344 | ### Removed 345 | 346 | - Nothing. 347 | 348 | ### Fixed 349 | 350 | - Nothing. 351 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018, Zend Technologies USA, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zend-test 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-test](https://github.com/laminas/laminas-test). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-test.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-test) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-test/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-test?branch=master) 9 | 10 | zend-test provides tools to facilitate unit testing of your Zend 11 | Framework applications. At this time, we offer facilities to enable testing of 12 | your Zend Framework MVC applications. [PHPUnit](https://phpunit.de/) is the only 13 | library supported currently. 14 | 15 | - File issues at https://github.com/zendframework/zend-test/issues 16 | - Ask questions at https://discourse.zendframework.com/c/questions/components or at https://zendframework.slack.com ([register](https://zendframework-slack.herokuapp.com)) 17 | - Documentation is at https://docs.zendframework.com/zend-test/ 18 | -------------------------------------------------------------------------------- /autoload/phpunit-class-aliases.php: -------------------------------------------------------------------------------- 1 | = 0 26 | ) { 27 | class_alias(\Zend\Test\PHPUnit\TestCaseTypeHintTrait::class, \Zend\Test\PHPUnit\TestCaseTrait::class); 28 | } else { 29 | class_alias(\Zend\Test\PHPUnit\TestCaseNoTypeHintTrait::class, \Zend\Test\PHPUnit\TestCaseTrait::class); 30 | } 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-test", 3 | "description": "Tools to facilitate unit testing of zend-mvc applications", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zendframework", 7 | "zf", 8 | "test" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-test/", 12 | "issues": "https://github.com/zendframework/zend-test/issues", 13 | "source": "https://github.com/zendframework/zend-test", 14 | "rss": "https://github.com/zendframework/zend-test/releases.atom", 15 | "slack": "https://zendframework-slack.herokuapp.com", 16 | "forum": "https://discourse.zendframework.com/c/questions/components" 17 | }, 18 | "require": { 19 | "php": "^5.6 || ^7.0", 20 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", 21 | "sebastian/version": "^1.0.4 || ^2.0", 22 | "zendframework/zend-console": "^2.6", 23 | "zendframework/zend-dom": "^2.6", 24 | "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", 25 | "zendframework/zend-http": "^2.8.3", 26 | "zendframework/zend-mvc": "^3.0", 27 | "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", 28 | "zendframework/zend-stdlib": "^2.7 || ^3.0", 29 | "zendframework/zend-uri": "^2.5", 30 | "zendframework/zend-view": "^2.6.3" 31 | }, 32 | "require-dev": { 33 | "mikey179/vfsstream": "~1.2", 34 | "zendframework/zend-coding-standard": "~1.0.0", 35 | "zendframework/zend-i18n": "^2.6", 36 | "zendframework/zend-log": "^2.7.1", 37 | "zendframework/zend-modulemanager": "^2.7.1", 38 | "zendframework/zend-mvc-console": "^1.1.8", 39 | "zendframework/zend-mvc-plugin-flashmessenger": "^0.1.0", 40 | "zendframework/zend-serializer": "^2.6.1", 41 | "zendframework/zend-session": "^2.8.5", 42 | "zendframework/zend-validator": "^2.8" 43 | }, 44 | "suggest": { 45 | "zendframework/zend-mvc-console": "^1.1.8, to test MVC <-> console integration" 46 | }, 47 | "autoload": { 48 | "files": [ 49 | "autoload/phpunit-class-aliases.php" 50 | ], 51 | "psr-4": { 52 | "Zend\\Test\\": "src/" 53 | } 54 | }, 55 | "autoload-dev": { 56 | "psr-4": { 57 | "ZendTest\\Test\\": "test/" 58 | } 59 | }, 60 | "config": { 61 | "sort-packages": true 62 | }, 63 | "extra": { 64 | "branch-alias": { 65 | "dev-master": "3.3.x-dev", 66 | "dev-develop": "3.4.x-dev" 67 | } 68 | }, 69 | "scripts": { 70 | "check": [ 71 | "@cs-check", 72 | "@test" 73 | ], 74 | "cs-check": "phpcs", 75 | "cs-fix": "phpcbf", 76 | "test": "phpunit --colors", 77 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/PHPUnit/Controller/AbstractConsoleControllerTestCase.php: -------------------------------------------------------------------------------- 1 | getResponse(); 31 | if (false === stripos($response->getContent(), $match)) { 32 | throw new ExpectationFailedException($this->createFailureMessage( 33 | sprintf( 34 | 'Failed asserting output CONTAINS content "%s", actual content is "%s"', 35 | $match, 36 | $response->getContent() 37 | ) 38 | )); 39 | } 40 | $this->assertNotSame(false, stripos($response->getContent(), $match)); 41 | } 42 | 43 | /** 44 | * Assert console output not contain content 45 | * 46 | * @param string $match content that should be contained in matched nodes 47 | * @return void 48 | */ 49 | public function assertNotConsoleOutputContains($match) 50 | { 51 | $response = $this->getResponse(); 52 | if (false !== stripos($response->getContent(), $match)) { 53 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 54 | 'Failed asserting output DOES NOT CONTAIN content "%s"', 55 | $match 56 | ))); 57 | } 58 | $this->assertSame(false, stripos($response->getContent(), $match)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/PHPUnit/Controller/AbstractControllerTestCase.php: -------------------------------------------------------------------------------- 1 | usedConsoleBackup = Console::isConsole(); 64 | $this->reset(); 65 | } 66 | 67 | /** 68 | * Restore params 69 | * 70 | * @internal 71 | */ 72 | protected function tearDownCompat() 73 | { 74 | Console::overrideIsConsole($this->usedConsoleBackup); 75 | 76 | // Prevent memory leak 77 | $this->reset(); 78 | } 79 | 80 | /** 81 | * Create a failure message. 82 | * 83 | * If $traceError is true, appends exception details, if any. 84 | * 85 | * @param string $message 86 | * @return string 87 | */ 88 | protected function createFailureMessage($message) 89 | { 90 | if (true !== $this->traceError) { 91 | return $message; 92 | } 93 | 94 | $exception = $this->getApplication()->getMvcEvent()->getParam('exception'); 95 | if (! $exception instanceof \Throwable && ! $exception instanceof \Exception) { 96 | return $message; 97 | } 98 | 99 | $messages = []; 100 | do { 101 | $messages[] = sprintf( 102 | "Exception '%s' with message '%s' in %s:%d", 103 | get_class($exception), 104 | $exception->getMessage(), 105 | $exception->getFile(), 106 | $exception->getLine() 107 | ); 108 | } while ($exception = $exception->getPrevious()); 109 | 110 | return sprintf("%s\n\nExceptions raised:\n%s\n", $message, implode("\n\n", $messages)); 111 | } 112 | 113 | /** 114 | * Get the trace error flag 115 | * @return bool 116 | */ 117 | public function getTraceError() 118 | { 119 | return $this->traceError; 120 | } 121 | 122 | /** 123 | * Set the trace error flag 124 | * @param bool $traceError 125 | * @return AbstractControllerTestCase 126 | */ 127 | public function setTraceError($traceError) 128 | { 129 | $this->traceError = $traceError; 130 | 131 | return $this; 132 | } 133 | 134 | /** 135 | * Get the usage of the console router or not 136 | * @return bool $boolean 137 | */ 138 | public function getUseConsoleRequest() 139 | { 140 | return $this->useConsoleRequest; 141 | } 142 | 143 | /** 144 | * Set the usage of the console router or not 145 | * @param bool $boolean 146 | * @return AbstractControllerTestCase 147 | */ 148 | public function setUseConsoleRequest($boolean) 149 | { 150 | $this->useConsoleRequest = (bool) $boolean; 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * Get the application config 157 | * @return array the application config 158 | */ 159 | public function getApplicationConfig() 160 | { 161 | return $this->applicationConfig; 162 | } 163 | 164 | /** 165 | * Set the application config 166 | * @param array $applicationConfig 167 | * @return AbstractControllerTestCase 168 | * @throws LogicException 169 | */ 170 | public function setApplicationConfig($applicationConfig) 171 | { 172 | if (null !== $this->application && null !== $this->applicationConfig) { 173 | throw new LogicException( 174 | 'Application config can not be set, the application is already built' 175 | ); 176 | } 177 | 178 | // do not cache module config on testing environment 179 | if (isset($applicationConfig['module_listener_options']['config_cache_enabled'])) { 180 | $applicationConfig['module_listener_options']['config_cache_enabled'] = false; 181 | } 182 | $this->applicationConfig = $applicationConfig; 183 | 184 | return $this; 185 | } 186 | 187 | /** 188 | * Get the application object 189 | * @return \Zend\Mvc\ApplicationInterface 190 | */ 191 | public function getApplication() 192 | { 193 | if ($this->application) { 194 | return $this->application; 195 | } 196 | $appConfig = $this->applicationConfig; 197 | Console::overrideIsConsole($this->getUseConsoleRequest()); 198 | $this->application = Application::init($appConfig); 199 | 200 | $events = $this->application->getEventManager(); 201 | $this->application->getServiceManager()->get('SendResponseListener')->detach($events); 202 | 203 | return $this->application; 204 | } 205 | 206 | /** 207 | * Get the service manager of the application object 208 | * @return \Zend\ServiceManager\ServiceManager 209 | */ 210 | public function getApplicationServiceLocator() 211 | { 212 | return $this->getApplication()->getServiceManager(); 213 | } 214 | 215 | /** 216 | * Get the application request object 217 | * @return \Zend\Stdlib\RequestInterface 218 | */ 219 | public function getRequest() 220 | { 221 | return $this->getApplication()->getRequest(); 222 | } 223 | 224 | /** 225 | * Get the application response object 226 | * @return ResponseInterface 227 | */ 228 | public function getResponse() 229 | { 230 | return $this->getApplication()->getMvcEvent()->getResponse(); 231 | } 232 | 233 | /** 234 | * Set the request URL 235 | * 236 | * @param string $url 237 | * @param string|null $method 238 | * @param array|null $params 239 | * @return AbstractControllerTestCase 240 | */ 241 | public function url($url, $method = HttpRequest::METHOD_GET, $params = []) 242 | { 243 | $request = $this->getRequest(); 244 | if ($this->useConsoleRequest) { 245 | preg_match_all('/(--\S+[= ]"[^\s"]*\s*[^\s"]*")|(\S+)/', $url, $matches); 246 | $params = str_replace([' "', '"'], ['=', ''], $matches[0]); 247 | $request->params()->exchangeArray($params); 248 | 249 | return $this; 250 | } 251 | 252 | $query = $request->getQuery()->toArray(); 253 | $post = $request->getPost()->toArray(); 254 | $uri = new HttpUri($url); 255 | $queryString = $uri->getQuery(); 256 | 257 | if ($queryString) { 258 | parse_str($queryString, $query); 259 | } 260 | 261 | if ($params) { 262 | switch ($method) { 263 | case HttpRequest::METHOD_POST: 264 | $post = $params; 265 | break; 266 | case HttpRequest::METHOD_GET: 267 | case HttpRequest::METHOD_DELETE: 268 | $query = array_merge($query, $params); 269 | break; 270 | case HttpRequest::METHOD_PUT: 271 | case HttpRequest::METHOD_PATCH: 272 | $content = http_build_query($params); 273 | $request->setContent($content); 274 | break; 275 | default: 276 | trigger_error( 277 | 'Additional params is only supported by GET, POST, PUT and PATCH HTTP method', 278 | E_USER_NOTICE 279 | ); 280 | } 281 | } 282 | 283 | $request->setMethod($method); 284 | $request->setQuery(new Parameters($query)); 285 | $request->setPost(new Parameters($post)); 286 | $request->setUri($uri); 287 | $request->setRequestUri($uri->getPath()); 288 | 289 | return $this; 290 | } 291 | 292 | /** 293 | * Dispatch the MVC with a URL 294 | * Accept a HTTP (simulate a customer action) or console route. 295 | * 296 | * The URL provided set the request URI in the request object. 297 | * 298 | * @param string $url 299 | * @param string|null $method 300 | * @param array|null $params 301 | * @throws \Exception 302 | */ 303 | public function dispatch($url, $method = null, $params = [], $isXmlHttpRequest = false) 304 | { 305 | if (! isset($method) 306 | && $this->getRequest() instanceof HttpRequest 307 | && $requestMethod = $this->getRequest()->getMethod() 308 | ) { 309 | $method = $requestMethod; 310 | } elseif (! isset($method)) { 311 | $method = HttpRequest::METHOD_GET; 312 | } 313 | 314 | if ($isXmlHttpRequest) { 315 | $headers = $this->getRequest()->getHeaders(); 316 | $headers->addHeaderLine('X_REQUESTED_WITH', 'XMLHttpRequest'); 317 | } 318 | 319 | $this->url($url, $method, $params); 320 | $this->getApplication()->run(); 321 | } 322 | 323 | /** 324 | * Reset the request 325 | * 326 | * @return AbstractControllerTestCase 327 | */ 328 | public function reset($keepPersistence = false) 329 | { 330 | // force to re-create all components 331 | $this->application = null; 332 | 333 | // reset server data 334 | if (! $keepPersistence) { 335 | // Do not create a global session variable if it doesn't already 336 | // exist. Otherwise calling this function could mark tests risky, 337 | // as it changes global state. 338 | if (array_key_exists('_SESSION', $GLOBALS)) { 339 | $_SESSION = []; 340 | } 341 | $_COOKIE = []; 342 | } 343 | 344 | $_GET = []; 345 | $_POST = []; 346 | 347 | // reset singleton 348 | if (class_exists(StaticEventManager::class)) { 349 | StaticEventManager::resetInstance(); 350 | } 351 | 352 | return $this; 353 | } 354 | 355 | /** 356 | * Trigger an application event 357 | * 358 | * @param string $eventName 359 | * @return \Zend\EventManager\ResponseCollection 360 | */ 361 | public function triggerApplicationEvent($eventName) 362 | { 363 | $events = $this->getApplication()->getEventManager(); 364 | $event = $this->getApplication()->getMvcEvent(); 365 | 366 | if ($eventName != MvcEvent::EVENT_ROUTE && $eventName != MvcEvent::EVENT_DISPATCH) { 367 | return $events->trigger($eventName, $event); 368 | } 369 | 370 | $shortCircuit = function ($r) use ($event) { 371 | if ($r instanceof ResponseInterface) { 372 | return true; 373 | } 374 | 375 | if ($event->getError()) { 376 | return true; 377 | } 378 | 379 | return false; 380 | }; 381 | 382 | $event->setName($eventName); 383 | return $events->triggerEventUntil($shortCircuit, $event); 384 | } 385 | 386 | /** 387 | * Assert modules were loaded with the module manager 388 | * 389 | * @param array $modules 390 | */ 391 | public function assertModulesLoaded(array $modules) 392 | { 393 | $moduleManager = $this->getApplicationServiceLocator()->get('ModuleManager'); 394 | $modulesLoaded = $moduleManager->getModules(); 395 | $list = array_diff($modules, $modulesLoaded); 396 | if ($list) { 397 | throw new ExpectationFailedException($this->createFailureMessage( 398 | sprintf('Several modules are not loaded "%s"', implode(', ', $list)) 399 | )); 400 | } 401 | $this->assertEquals(count($list), 0); 402 | } 403 | 404 | /** 405 | * Assert modules were not loaded with the module manager 406 | * 407 | * @param array $modules 408 | */ 409 | public function assertNotModulesLoaded(array $modules) 410 | { 411 | $moduleManager = $this->getApplicationServiceLocator()->get('ModuleManager'); 412 | $modulesLoaded = $moduleManager->getModules(); 413 | $list = array_intersect($modules, $modulesLoaded); 414 | if ($list) { 415 | throw new ExpectationFailedException($this->createFailureMessage( 416 | sprintf('Several modules WAS not loaded "%s"', implode(', ', $list)) 417 | )); 418 | } 419 | $this->assertEquals(count($list), 0); 420 | } 421 | 422 | /** 423 | * Retrieve the response status code 424 | * 425 | * @return int 426 | */ 427 | protected function getResponseStatusCode() 428 | { 429 | $response = $this->getResponse(); 430 | if (! $this->useConsoleRequest) { 431 | return $response->getStatusCode(); 432 | } 433 | 434 | $match = $response->getErrorLevel(); 435 | if (null === $match) { 436 | $match = 0; 437 | } 438 | 439 | return $match; 440 | } 441 | 442 | /** 443 | * Assert response status code 444 | * 445 | * @param int $code 446 | */ 447 | public function assertResponseStatusCode($code) 448 | { 449 | if ($this->useConsoleRequest) { 450 | if (! in_array($code, [0, 1])) { 451 | throw new ExpectationFailedException($this->createFailureMessage( 452 | 'Console status code assert value must be O (valid) or 1 (error)' 453 | )); 454 | } 455 | } 456 | $match = $this->getResponseStatusCode(); 457 | if ($code != $match) { 458 | throw new ExpectationFailedException($this->createFailureMessage( 459 | sprintf('Failed asserting response code "%s", actual status code is "%s"', $code, $match) 460 | )); 461 | } 462 | $this->assertEquals($code, $match); 463 | } 464 | 465 | /** 466 | * Assert not response status code 467 | * 468 | * @param int $code 469 | */ 470 | public function assertNotResponseStatusCode($code) 471 | { 472 | if ($this->useConsoleRequest) { 473 | if (! in_array($code, [0, 1])) { 474 | throw new ExpectationFailedException($this->createFailureMessage( 475 | 'Console status code assert value must be O (valid) or 1 (error)' 476 | )); 477 | } 478 | } 479 | $match = $this->getResponseStatusCode(); 480 | if ($code == $match) { 481 | throw new ExpectationFailedException($this->createFailureMessage( 482 | sprintf('Failed asserting response code was NOT "%s"', $code) 483 | )); 484 | } 485 | $this->assertNotEquals($code, $match); 486 | } 487 | 488 | /** 489 | * Assert the application exception and message 490 | * 491 | * @param $type application exception type 492 | * @param $message application exception message 493 | */ 494 | public function assertApplicationException($type, $message = null) 495 | { 496 | $exception = $this->getApplication()->getMvcEvent()->getParam('exception'); 497 | if (! $exception) { 498 | throw new ExpectationFailedException($this->createFailureMessage( 499 | 'Failed asserting application exception, exception not exist' 500 | )); 501 | } 502 | if (true === $this->traceError) { 503 | // set exception as null because we know and have assert the exception 504 | $this->getApplication()->getMvcEvent()->setParam('exception', null); 505 | } 506 | 507 | if (! method_exists($this, 'expectException')) { 508 | // For old PHPUnit 4 509 | $this->setExpectedException($type, $message); 510 | } else { 511 | $this->expectException($type); 512 | if (! empty($message)) { 513 | $this->expectExceptionMessage($message); 514 | } 515 | } 516 | 517 | throw $exception; 518 | } 519 | 520 | /** 521 | * Get the full current controller class name 522 | * 523 | * @return string 524 | */ 525 | protected function getControllerFullClassName() 526 | { 527 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 528 | if (! $routeMatch) { 529 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 530 | } 531 | $controllerIdentifier = $routeMatch->getParam('controller'); 532 | $controllerManager = $this->getApplicationServiceLocator()->get('ControllerManager'); 533 | $controllerClass = $controllerManager->get($controllerIdentifier); 534 | 535 | return get_class($controllerClass); 536 | } 537 | 538 | /** 539 | * Assert that the application route match used the given module 540 | * 541 | * @param string $module 542 | */ 543 | public function assertModuleName($module) 544 | { 545 | $controllerClass = $this->getControllerFullClassName(); 546 | $match = substr($controllerClass, 0, strpos($controllerClass, '\\')); 547 | $match = strtolower($match); 548 | $module = strtolower($module); 549 | if ($module != $match) { 550 | throw new ExpectationFailedException($this->createFailureMessage( 551 | sprintf('Failed asserting module name "%s", actual module name is "%s"', $module, $match) 552 | )); 553 | } 554 | $this->assertEquals($module, $match); 555 | } 556 | 557 | /** 558 | * Assert that the application route match used NOT the given module 559 | * 560 | * @param string $module 561 | */ 562 | public function assertNotModuleName($module) 563 | { 564 | $controllerClass = $this->getControllerFullClassName(); 565 | $match = substr($controllerClass, 0, strpos($controllerClass, '\\')); 566 | $match = strtolower($match); 567 | $module = strtolower($module); 568 | if ($module == $match) { 569 | throw new ExpectationFailedException($this->createFailureMessage( 570 | sprintf('Failed asserting module was NOT "%s"', $module) 571 | )); 572 | } 573 | $this->assertNotEquals($module, $match); 574 | } 575 | 576 | /** 577 | * Assert that the application route match used the given controller class 578 | * 579 | * @param string $controller 580 | */ 581 | public function assertControllerClass($controller) 582 | { 583 | $controllerClass = $this->getControllerFullClassName(); 584 | $match = substr($controllerClass, strrpos($controllerClass, '\\') + 1); 585 | $match = strtolower($match); 586 | $controller = strtolower($controller); 587 | if ($controller != $match) { 588 | throw new ExpectationFailedException($this->createFailureMessage( 589 | sprintf('Failed asserting controller class "%s", actual controller class is "%s"', $controller, $match) 590 | )); 591 | } 592 | $this->assertEquals($controller, $match); 593 | } 594 | 595 | /** 596 | * Assert that the application route match used NOT the given controller class 597 | * 598 | * @param string $controller 599 | */ 600 | public function assertNotControllerClass($controller) 601 | { 602 | $controllerClass = $this->getControllerFullClassName(); 603 | $match = substr($controllerClass, strrpos($controllerClass, '\\') + 1); 604 | $match = strtolower($match); 605 | $controller = strtolower($controller); 606 | if ($controller == $match) { 607 | throw new ExpectationFailedException($this->createFailureMessage( 608 | sprintf('Failed asserting controller class was NOT "%s"', $controller) 609 | )); 610 | } 611 | $this->assertNotEquals($controller, $match); 612 | } 613 | 614 | /** 615 | * Assert that the application route match used the given controller name 616 | * 617 | * @param string $controller 618 | */ 619 | public function assertControllerName($controller) 620 | { 621 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 622 | if (! $routeMatch) { 623 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 624 | } 625 | $match = $routeMatch->getParam('controller'); 626 | $match = strtolower($match); 627 | $controller = strtolower($controller); 628 | if ($controller != $match) { 629 | throw new ExpectationFailedException($this->createFailureMessage( 630 | sprintf('Failed asserting controller name "%s", actual controller name is "%s"', $controller, $match) 631 | )); 632 | } 633 | $this->assertEquals($controller, $match); 634 | } 635 | 636 | /** 637 | * Assert that the application route match used NOT the given controller name 638 | * 639 | * @param string $controller 640 | */ 641 | public function assertNotControllerName($controller) 642 | { 643 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 644 | if (! $routeMatch) { 645 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 646 | } 647 | $match = $routeMatch->getParam('controller'); 648 | $match = strtolower($match); 649 | $controller = strtolower($controller); 650 | if ($controller == $match) { 651 | throw new ExpectationFailedException($this->createFailureMessage( 652 | sprintf('Failed asserting controller name was NOT "%s"', $controller) 653 | )); 654 | } 655 | $this->assertNotEquals($controller, $match); 656 | } 657 | 658 | /** 659 | * Assert that the application route match used the given action 660 | * 661 | * @param string $action 662 | */ 663 | public function assertActionName($action) 664 | { 665 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 666 | if (! $routeMatch) { 667 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 668 | } 669 | $match = $routeMatch->getParam('action'); 670 | $match = strtolower($match); 671 | $action = strtolower($action); 672 | if ($action != $match) { 673 | throw new ExpectationFailedException($this->createFailureMessage( 674 | sprintf('Failed asserting action name "%s", actual action name is "%s"', $action, $match) 675 | )); 676 | } 677 | $this->assertEquals($action, $match); 678 | } 679 | 680 | /** 681 | * Assert that the application route match used NOT the given action 682 | * 683 | * @param string $action 684 | */ 685 | public function assertNotActionName($action) 686 | { 687 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 688 | if (! $routeMatch) { 689 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 690 | } 691 | $match = $routeMatch->getParam('action'); 692 | $match = strtolower($match); 693 | $action = strtolower($action); 694 | if ($action == $match) { 695 | throw new ExpectationFailedException($this->createFailureMessage( 696 | sprintf('Failed asserting action name was NOT "%s"', $action) 697 | )); 698 | } 699 | $this->assertNotEquals($action, $match); 700 | } 701 | 702 | /** 703 | * Assert that the application route match used the given route name 704 | * 705 | * @param string $route 706 | */ 707 | public function assertMatchedRouteName($route) 708 | { 709 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 710 | if (! $routeMatch) { 711 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 712 | } 713 | $match = $routeMatch->getMatchedRouteName(); 714 | $match = strtolower($match); 715 | $route = strtolower($route); 716 | if ($route != $match) { 717 | throw new ExpectationFailedException($this->createFailureMessage( 718 | sprintf( 719 | 'Failed asserting matched route name was "%s", actual matched route name is "%s"', 720 | $route, 721 | $match 722 | ) 723 | )); 724 | } 725 | $this->assertEquals($route, $match); 726 | } 727 | 728 | /** 729 | * Assert that the application route match used NOT the given route name 730 | * 731 | * @param string $route 732 | */ 733 | public function assertNotMatchedRouteName($route) 734 | { 735 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 736 | if (! $routeMatch) { 737 | throw new ExpectationFailedException($this->createFailureMessage('No route matched')); 738 | } 739 | $match = $routeMatch->getMatchedRouteName(); 740 | $match = strtolower($match); 741 | $route = strtolower($route); 742 | if ($route == $match) { 743 | throw new ExpectationFailedException($this->createFailureMessage( 744 | sprintf('Failed asserting route matched was NOT "%s"', $route) 745 | )); 746 | } 747 | $this->assertNotEquals($route, $match); 748 | } 749 | 750 | /** 751 | * Assert that the application did not match any route 752 | */ 753 | public function assertNoMatchedRoute() 754 | { 755 | $routeMatch = $this->getApplication()->getMvcEvent()->getRouteMatch(); 756 | if ($routeMatch) { 757 | $match = $routeMatch->getMatchedRouteName(); 758 | $match = strtolower($match); 759 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 760 | 'Failed asserting that no route matched, actual matched route name is "%s"', 761 | $match 762 | ))); 763 | } 764 | $this->assertNull($routeMatch); 765 | } 766 | 767 | /** 768 | * Assert template name 769 | * Assert that a template was used somewhere in the view model tree 770 | * 771 | * @param string $templateName 772 | */ 773 | public function assertTemplateName($templateName) 774 | { 775 | $viewModel = $this->getApplication()->getMvcEvent()->getViewModel(); 776 | $this->assertTrue($this->searchTemplates($viewModel, $templateName)); 777 | } 778 | 779 | /** 780 | * Assert not template name 781 | * Assert that a template was not used somewhere in the view model tree 782 | * 783 | * @param string $templateName 784 | */ 785 | public function assertNotTemplateName($templateName) 786 | { 787 | $viewModel = $this->getApplication()->getMvcEvent()->getViewModel(); 788 | $this->assertFalse($this->searchTemplates($viewModel, $templateName)); 789 | } 790 | 791 | /** 792 | * Recursively search a view model and it's children for the given templateName 793 | * 794 | * @param \Zend\View\Model\ModelInterface $viewModel 795 | * @param string $templateName 796 | * @return boolean 797 | */ 798 | protected function searchTemplates($viewModel, $templateName) 799 | { 800 | if ($viewModel->getTemplate($templateName) == $templateName) { 801 | return true; 802 | } 803 | foreach ($viewModel->getChildren() as $child) { 804 | return $this->searchTemplates($child, $templateName); 805 | } 806 | 807 | return false; 808 | } 809 | } 810 | -------------------------------------------------------------------------------- /src/PHPUnit/Controller/AbstractHttpControllerTestCase.php: -------------------------------------------------------------------------------- 1 | getResponse(); 37 | $headers = $response->getHeaders(); 38 | $responseHeader = $headers->get($header, false); 39 | return $responseHeader; 40 | } 41 | 42 | /** 43 | * Assert response has the given reason phrase 44 | * 45 | * @param string $phrase 46 | */ 47 | public function assertResponseReasonPhrase($phrase) 48 | { 49 | $this->assertEquals($phrase, $this->getResponse()->getReasonPhrase()); 50 | } 51 | 52 | /** 53 | * Assert response header exists 54 | * 55 | * @param string $header 56 | */ 57 | public function assertHasResponseHeader($header) 58 | { 59 | $responseHeader = $this->getResponseHeader($header); 60 | if (false === $responseHeader) { 61 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 62 | 'Failed asserting response header "%s" found', 63 | $header 64 | ))); 65 | } 66 | $this->assertNotEquals(false, $responseHeader); 67 | } 68 | 69 | /** 70 | * Assert response header does not exist 71 | * 72 | * @param string $header 73 | */ 74 | public function assertNotHasResponseHeader($header) 75 | { 76 | $responseHeader = $this->getResponseHeader($header); 77 | if (false !== $responseHeader) { 78 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 79 | 'Failed asserting response header "%s" WAS NOT found', 80 | $header 81 | ))); 82 | } 83 | $this->assertFalse($responseHeader); 84 | } 85 | 86 | /** 87 | * Assert response header exists and contains the given string 88 | * 89 | * @param string $header 90 | * @param string $match 91 | */ 92 | public function assertResponseHeaderContains($header, $match) 93 | { 94 | $responseHeader = $this->getResponseHeader($header); 95 | if (! $responseHeader) { 96 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 97 | 'Failed asserting response header, header "%s" doesn\'t exist', 98 | $header 99 | ))); 100 | } 101 | 102 | if (! $responseHeader instanceof \ArrayIterator) { 103 | $responseHeader = [$responseHeader]; 104 | } 105 | 106 | $headerMatched = false; 107 | 108 | foreach ($responseHeader as $currentHeader) { 109 | if ($match == $currentHeader->getFieldValue()) { 110 | $headerMatched = true; 111 | break; 112 | } 113 | } 114 | 115 | if (! $headerMatched) { 116 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 117 | 'Failed asserting response header "%s" exists and contains "%s", actual content is "%s"', 118 | $header, 119 | $match, 120 | $currentHeader->getFieldValue() 121 | ))); 122 | } 123 | 124 | $this->assertEquals($match, $currentHeader->getFieldValue()); 125 | } 126 | 127 | /** 128 | * Assert response header exists and contains the given string 129 | * 130 | * @param string $header 131 | * @param string $match 132 | */ 133 | public function assertNotResponseHeaderContains($header, $match) 134 | { 135 | $responseHeader = $this->getResponseHeader($header); 136 | if (! $responseHeader) { 137 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 138 | 'Failed asserting response header, header "%s" doesn\'t exist', 139 | $header 140 | ))); 141 | } 142 | 143 | if (! $responseHeader instanceof \ArrayIterator) { 144 | $responseHeader = [$responseHeader]; 145 | } 146 | 147 | foreach ($responseHeader as $currentHeader) { 148 | if ($match == $currentHeader->getFieldValue()) { 149 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 150 | 'Failed asserting response header "%s" DOES NOT CONTAIN "%s"', 151 | $header, 152 | $match 153 | ))); 154 | } 155 | } 156 | 157 | $this->assertNotEquals($match, $currentHeader->getFieldValue()); 158 | } 159 | 160 | /** 161 | * Assert response header exists and matches the given pattern 162 | * 163 | * @param string $header 164 | * @param string $pattern 165 | */ 166 | public function assertResponseHeaderRegex($header, $pattern) 167 | { 168 | $responseHeader = $this->getResponseHeader($header); 169 | if (! $responseHeader) { 170 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 171 | 'Failed asserting response header, header "%s" doesn\'t exist', 172 | $header 173 | ))); 174 | } 175 | 176 | if (! $responseHeader instanceof \ArrayIterator) { 177 | $responseHeader = [$responseHeader]; 178 | } 179 | 180 | $headerMatched = false; 181 | 182 | foreach ($responseHeader as $currentHeader) { 183 | $headerMatched = (bool) preg_match($pattern, $currentHeader->getFieldValue()); 184 | 185 | if ($headerMatched) { 186 | break; 187 | } 188 | } 189 | 190 | if (! $headerMatched) { 191 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 192 | 'Failed asserting response header "%s" exists and matches regex "%s", actual content is "%s"', 193 | $header, 194 | $pattern, 195 | $currentHeader->getFieldValue() 196 | ))); 197 | } 198 | 199 | $this->assertTrue($headerMatched); 200 | } 201 | 202 | /** 203 | * Assert response header does not exist and/or does not match the given regex 204 | * 205 | * @param string $header 206 | * @param string $pattern 207 | */ 208 | public function assertNotResponseHeaderRegex($header, $pattern) 209 | { 210 | $responseHeader = $this->getResponseHeader($header); 211 | if (! $responseHeader) { 212 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 213 | 'Failed asserting response header, header "%s" doesn\'t exist', 214 | $header 215 | ))); 216 | } 217 | 218 | if (! $responseHeader instanceof \ArrayIterator) { 219 | $responseHeader = [$responseHeader]; 220 | } 221 | 222 | $headerMatched = false; 223 | 224 | foreach ($responseHeader as $currentHeader) { 225 | $headerMatched = (bool) preg_match($pattern, $currentHeader->getFieldValue()); 226 | 227 | if ($headerMatched) { 228 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 229 | 'Failed asserting response header "%s" DOES NOT MATCH regex "%s"', 230 | $header, 231 | $pattern 232 | ))); 233 | } 234 | } 235 | 236 | $this->assertFalse($headerMatched); 237 | } 238 | 239 | /** 240 | * Assert that response is a redirect 241 | */ 242 | public function assertRedirect() 243 | { 244 | $responseHeader = $this->getResponseHeader('Location'); 245 | if (false === $responseHeader) { 246 | throw new ExpectationFailedException($this->createFailureMessage( 247 | 'Failed asserting response is a redirect' 248 | )); 249 | } 250 | $this->assertNotEquals(false, $responseHeader); 251 | } 252 | 253 | /** 254 | * Assert that response is NOT a redirect 255 | */ 256 | public function assertNotRedirect() 257 | { 258 | $responseHeader = $this->getResponseHeader('Location'); 259 | if (false !== $responseHeader) { 260 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 261 | 'Failed asserting response is NOT a redirect, actual redirection is "%s"', 262 | $responseHeader->getFieldValue() 263 | ))); 264 | } 265 | $this->assertFalse($responseHeader); 266 | } 267 | 268 | /** 269 | * Assert that response redirects to given URL 270 | * 271 | * @param string $url 272 | */ 273 | public function assertRedirectTo($url) 274 | { 275 | $responseHeader = $this->getResponseHeader('Location'); 276 | if (! $responseHeader) { 277 | throw new ExpectationFailedException($this->createFailureMessage( 278 | 'Failed asserting response is a redirect' 279 | )); 280 | } 281 | if ($url != $responseHeader->getFieldValue()) { 282 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 283 | 'Failed asserting response redirects to "%s", actual redirection is "%s"', 284 | $url, 285 | $responseHeader->getFieldValue() 286 | ))); 287 | } 288 | $this->assertEquals($url, $responseHeader->getFieldValue()); 289 | } 290 | 291 | /** 292 | * Assert that response does not redirect to given URL 293 | * 294 | * @param string $url 295 | */ 296 | public function assertNotRedirectTo($url) 297 | { 298 | $responseHeader = $this->getResponseHeader('Location'); 299 | if (! $responseHeader) { 300 | throw new ExpectationFailedException($this->createFailureMessage( 301 | 'Failed asserting response is a redirect' 302 | )); 303 | } 304 | if ($url == $responseHeader->getFieldValue()) { 305 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 306 | 'Failed asserting response redirects to "%s"', 307 | $url 308 | ))); 309 | } 310 | $this->assertNotEquals($url, $responseHeader->getFieldValue()); 311 | } 312 | 313 | /** 314 | * Assert that redirect location matches pattern 315 | * 316 | * @param string $pattern 317 | */ 318 | public function assertRedirectRegex($pattern) 319 | { 320 | $responseHeader = $this->getResponseHeader('Location'); 321 | if (! $responseHeader) { 322 | throw new ExpectationFailedException($this->createFailureMessage( 323 | 'Failed asserting response is a redirect' 324 | )); 325 | } 326 | if (! preg_match($pattern, $responseHeader->getFieldValue())) { 327 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 328 | 'Failed asserting response redirects to URL MATCHING "%s", actual redirection is "%s"', 329 | $pattern, 330 | $responseHeader->getFieldValue() 331 | ))); 332 | } 333 | $this->assertTrue((bool) preg_match($pattern, $responseHeader->getFieldValue())); 334 | } 335 | 336 | /** 337 | * Assert that redirect location does not match pattern 338 | * 339 | * @param string $pattern 340 | */ 341 | public function assertNotRedirectRegex($pattern) 342 | { 343 | $responseHeader = $this->getResponseHeader('Location'); 344 | if (! $responseHeader) { 345 | throw new ExpectationFailedException($this->createFailureMessage( 346 | 'Failed asserting response is a redirect' 347 | )); 348 | } 349 | if (preg_match($pattern, $responseHeader->getFieldValue())) { 350 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 351 | 'Failed asserting response DOES NOT redirect to URL MATCHING "%s"', 352 | $pattern 353 | ))); 354 | } 355 | $this->assertFalse((bool) preg_match($pattern, $responseHeader->getFieldValue())); 356 | } 357 | 358 | /** 359 | * Register XPath namespaces 360 | * 361 | * @param array $xpathNamespaces 362 | */ 363 | public function registerXpathNamespaces(array $xpathNamespaces) 364 | { 365 | $this->xpathNamespaces = $xpathNamespaces; 366 | } 367 | 368 | /** 369 | * Execute a DOM/XPath query 370 | * 371 | * @param string $path 372 | * @param bool $useXpath 373 | * @return Document\NodeList 374 | */ 375 | private function query($path, $useXpath = false) 376 | { 377 | $response = $this->getResponse(); 378 | $document = new Document($response->getContent()); 379 | 380 | if ($useXpath) { 381 | $document->registerXpathNamespaces($this->xpathNamespaces); 382 | } 383 | 384 | $result = Document\Query::execute( 385 | $path, 386 | $document, 387 | $useXpath ? Document\Query::TYPE_XPATH : Document\Query::TYPE_CSS 388 | ); 389 | 390 | return $result; 391 | } 392 | 393 | /** 394 | * Execute a xpath query 395 | * 396 | * @param string $path 397 | * @return array 398 | */ 399 | private function xpathQuery($path) 400 | { 401 | return $this->query($path, true); 402 | } 403 | 404 | /** 405 | * Count the dom query executed 406 | * 407 | * @param string $path 408 | * @return int 409 | */ 410 | private function queryCount($path) 411 | { 412 | return count($this->query($path, false)); 413 | } 414 | 415 | /** 416 | * Count the dom query executed 417 | * 418 | * @param string $path 419 | * @return int 420 | */ 421 | private function xpathQueryCount($path) 422 | { 423 | return count($this->xpathQuery($path)); 424 | } 425 | 426 | /** 427 | * Assert against DOM/XPath selection 428 | * 429 | * @param string $path 430 | * @param bool $useXpath 431 | */ 432 | private function queryAssertion($path, $useXpath = false) 433 | { 434 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 435 | $match = $this->$method($path); 436 | if (! $match > 0) { 437 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 438 | 'Failed asserting node DENOTED BY %s EXISTS', 439 | $path 440 | ))); 441 | } 442 | $this->assertTrue($match > 0); 443 | } 444 | 445 | /** 446 | * Assert against DOM selection 447 | * 448 | * @param string $path CSS selector path 449 | */ 450 | public function assertQuery($path) 451 | { 452 | $this->queryAssertion($path, false); 453 | } 454 | 455 | /** 456 | * Assert against XPath selection 457 | * 458 | * @param string $path XPath path 459 | */ 460 | public function assertXpathQuery($path) 461 | { 462 | $this->queryAssertion($path, true); 463 | } 464 | 465 | /** 466 | * Assert against DOM/XPath selection 467 | * 468 | * @param string $path CSS selector path 469 | * @param bool $useXpath 470 | */ 471 | private function notQueryAssertion($path, $useXpath = false) 472 | { 473 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 474 | $match = $this->$method($path); 475 | if ($match != 0) { 476 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 477 | 'Failed asserting node DENOTED BY %s DOES NOT EXIST', 478 | $path 479 | ))); 480 | } 481 | $this->assertEquals(0, $match); 482 | } 483 | 484 | /** 485 | * Assert against DOM selection 486 | * 487 | * @param string $path CSS selector path 488 | */ 489 | public function assertNotQuery($path) 490 | { 491 | $this->notQueryAssertion($path, false); 492 | } 493 | 494 | /** 495 | * Assert against XPath selection 496 | * 497 | * @param string $path XPath path 498 | */ 499 | public function assertNotXpathQuery($path) 500 | { 501 | $this->notQueryAssertion($path, true); 502 | } 503 | 504 | /** 505 | * Assert against DOM/XPath selection; should contain exact number of nodes 506 | * 507 | * @param string $path CSS selector path 508 | * @param string $count Number of nodes that should match 509 | * @param bool $useXpath 510 | */ 511 | private function queryCountAssertion($path, $count, $useXpath = false) 512 | { 513 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 514 | $match = $this->$method($path); 515 | if ($match != $count) { 516 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 517 | 'Failed asserting node DENOTED BY %s OCCURS EXACTLY %d times, actually occurs %d times', 518 | $path, 519 | $count, 520 | $match 521 | ))); 522 | } 523 | $this->assertEquals($match, $count); 524 | } 525 | 526 | /** 527 | * Assert against DOM selection; should contain exact number of nodes 528 | * 529 | * @param string $path CSS selector path 530 | * @param string $count Number of nodes that should match 531 | */ 532 | public function assertQueryCount($path, $count) 533 | { 534 | $this->queryCountAssertion($path, $count, false); 535 | } 536 | 537 | /** 538 | * Assert against XPath selection; should contain exact number of nodes 539 | * 540 | * @param string $path XPath path 541 | * @param string $count Number of nodes that should match 542 | */ 543 | public function assertXpathQueryCount($path, $count) 544 | { 545 | $this->queryCountAssertion($path, $count, true); 546 | } 547 | 548 | /** 549 | * Assert against DOM/XPath selection; should NOT contain exact number of nodes 550 | * 551 | * @param string $path CSS selector path 552 | * @param string $count Number of nodes that should NOT match 553 | * @param bool $useXpath 554 | */ 555 | private function notQueryCountAssertion($path, $count, $useXpath = false) 556 | { 557 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 558 | $match = $this->$method($path); 559 | if ($match == $count) { 560 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 561 | 'Failed asserting node DENOTED BY %s DOES NOT OCCUR EXACTLY %d times', 562 | $path, 563 | $count 564 | ))); 565 | } 566 | $this->assertNotEquals($match, $count); 567 | } 568 | 569 | /** 570 | * Assert against DOM selection; should NOT contain exact number of nodes 571 | * 572 | * @param string $path CSS selector path 573 | * @param string $count Number of nodes that should NOT match 574 | */ 575 | public function assertNotQueryCount($path, $count) 576 | { 577 | $this->notQueryCountAssertion($path, $count, false); 578 | } 579 | 580 | /** 581 | * Assert against XPath selection; should NOT contain exact number of nodes 582 | * 583 | * @param string $path XPath path 584 | * @param string $count Number of nodes that should NOT match 585 | */ 586 | public function assertNotXpathQueryCount($path, $count) 587 | { 588 | $this->notQueryCountAssertion($path, $count, true); 589 | } 590 | 591 | /** 592 | * Assert against DOM/XPath selection; should contain at least this number of nodes 593 | * 594 | * @param string $path CSS selector path 595 | * @param string $count Minimum number of nodes that should match 596 | * @param bool $useXpath 597 | */ 598 | private function queryCountMinAssertion($path, $count, $useXpath = false) 599 | { 600 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 601 | $match = $this->$method($path); 602 | if ($match < $count) { 603 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 604 | 'Failed asserting node DENOTED BY %s OCCURS AT LEAST %d times, actually occurs %d times', 605 | $path, 606 | $count, 607 | $match 608 | ))); 609 | } 610 | $this->assertTrue($match >= $count); 611 | } 612 | 613 | /** 614 | * Assert against DOM selection; should contain at least this number of nodes 615 | * 616 | * @param string $path CSS selector path 617 | * @param string $count Minimum number of nodes that should match 618 | */ 619 | public function assertQueryCountMin($path, $count) 620 | { 621 | $this->queryCountMinAssertion($path, $count, false); 622 | } 623 | 624 | /** 625 | * Assert against XPath selection; should contain at least this number of nodes 626 | * 627 | * @param string $path XPath path 628 | * @param string $count Minimum number of nodes that should match 629 | */ 630 | public function assertXpathQueryCountMin($path, $count) 631 | { 632 | $this->queryCountMinAssertion($path, $count, true); 633 | } 634 | 635 | /** 636 | * Assert against DOM/XPath selection; should contain no more than this number of nodes 637 | * 638 | * @param string $path CSS selector path 639 | * @param string $count Maximum number of nodes that should match 640 | * @param bool $useXpath 641 | */ 642 | private function queryCountMaxAssertion($path, $count, $useXpath = false) 643 | { 644 | $method = $useXpath ? 'xpathQueryCount' : 'queryCount'; 645 | $match = $this->$method($path); 646 | if ($match > $count) { 647 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 648 | 'Failed asserting node DENOTED BY %s OCCURS AT MOST %d times, actually occurs %d times', 649 | $path, 650 | $count, 651 | $match 652 | ))); 653 | } 654 | $this->assertTrue($match <= $count); 655 | } 656 | 657 | /** 658 | * Assert against DOM selection; should contain no more than this number of nodes 659 | * 660 | * @param string $path CSS selector path 661 | * @param string $count Maximum number of nodes that should match 662 | */ 663 | public function assertQueryCountMax($path, $count) 664 | { 665 | $this->queryCountMaxAssertion($path, $count, false); 666 | } 667 | 668 | /** 669 | * Assert against XPath selection; should contain no more than this number of nodes 670 | * 671 | * @param string $path XPath path 672 | * @param string $count Maximum number of nodes that should match 673 | */ 674 | public function assertXpathQueryCountMax($path, $count) 675 | { 676 | $this->queryCountMaxAssertion($path, $count, true); 677 | } 678 | 679 | /** 680 | * Assert against DOM/XPath selection; node should contain content 681 | * 682 | * @param string $path CSS selector path 683 | * @param string $match content that should be contained in matched nodes 684 | * @param bool $useXpath 685 | */ 686 | private function queryContentContainsAssertion($path, $match, $useXpath = false) 687 | { 688 | $result = $this->query($path, $useXpath); 689 | 690 | if ($result->count() == 0) { 691 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 692 | 'Failed asserting node DENOTED BY %s EXISTS', 693 | $path 694 | ))); 695 | } 696 | 697 | $nodeValues = []; 698 | 699 | foreach ($result as $node) { 700 | if ($node->nodeValue == $match) { 701 | $this->assertEquals($match, $node->nodeValue); 702 | return; 703 | } 704 | 705 | $nodeValues[] = $node->nodeValue; 706 | } 707 | 708 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 709 | 'Failed asserting node denoted by %s CONTAINS content "%s", Contents: [%s]', 710 | $path, 711 | $match, 712 | implode(',', $nodeValues) 713 | ))); 714 | } 715 | 716 | /** 717 | * Assert against DOM selection; node should contain content 718 | * 719 | * @param string $path CSS selector path 720 | * @param string $match content that should be contained in matched nodes 721 | */ 722 | public function assertQueryContentContains($path, $match) 723 | { 724 | $this->queryContentContainsAssertion($path, $match, false); 725 | } 726 | 727 | /** 728 | * Assert against XPath selection; node should contain content 729 | * 730 | * @param string $path XPath path 731 | * @param string $match content that should be contained in matched nodes 732 | */ 733 | public function assertXpathQueryContentContains($path, $match) 734 | { 735 | $this->queryContentContainsAssertion($path, $match, true); 736 | } 737 | 738 | /** 739 | * Assert against DOM/XPath selection; node should NOT contain content 740 | * 741 | * @param string $path CSS selector path 742 | * @param string $match content that should NOT be contained in matched nodes 743 | * @param bool $useXpath 744 | */ 745 | private function notQueryContentContainsAssertion($path, $match, $useXpath = false) 746 | { 747 | $result = $this->query($path, $useXpath); 748 | if ($result->count() == 0) { 749 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 750 | 'Failed asserting node DENOTED BY %s EXISTS', 751 | $path 752 | ))); 753 | } 754 | foreach ($result as $node) { 755 | if ($node->nodeValue == $match) { 756 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 757 | 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content "%s"', 758 | $path, 759 | $match 760 | ))); 761 | } 762 | } 763 | $currentValue = $node->nodeValue; 764 | $this->assertNotEquals($currentValue, $match); 765 | } 766 | 767 | /** 768 | * Assert against DOM selection; node should NOT contain content 769 | * 770 | * @param string $path CSS selector path 771 | * @param string $match content that should NOT be contained in matched nodes 772 | */ 773 | public function assertNotQueryContentContains($path, $match) 774 | { 775 | $this->notQueryContentContainsAssertion($path, $match, false); 776 | } 777 | 778 | /** 779 | * Assert against XPath selection; node should NOT contain content 780 | * 781 | * @param string $path XPath path 782 | * @param string $match content that should NOT be contained in matched nodes 783 | */ 784 | public function assertNotXpathQueryContentContains($path, $match) 785 | { 786 | $this->notQueryContentContainsAssertion($path, $match, true); 787 | } 788 | 789 | /** 790 | * Assert against DOM/XPath selection; node should match content 791 | * 792 | * @param string $path CSS selector path 793 | * @param string $pattern Pattern that should be contained in matched nodes 794 | * @param bool $useXpath 795 | */ 796 | private function queryContentRegexAssertion($path, $pattern, $useXpath = false) 797 | { 798 | $result = $this->query($path, $useXpath); 799 | if ($result->count() == 0) { 800 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 801 | 'Failed asserting node DENOTED BY %s EXISTS', 802 | $path 803 | ))); 804 | } 805 | 806 | $found = false; 807 | $nodeValues = []; 808 | 809 | foreach ($result as $node) { 810 | $nodeValues[] = $node->nodeValue; 811 | if (preg_match($pattern, $node->nodeValue)) { 812 | $found = true; 813 | break; 814 | } 815 | } 816 | 817 | if (! $found) { 818 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 819 | 'Failed asserting node denoted by %s CONTAINS content MATCHING "%s", actual content is "%s"', 820 | $path, 821 | $pattern, 822 | implode('', $nodeValues) 823 | ))); 824 | } 825 | 826 | $this->assertTrue($found); 827 | } 828 | 829 | /** 830 | * Assert against DOM selection; node should match content 831 | * 832 | * @param string $path CSS selector path 833 | * @param string $pattern Pattern that should be contained in matched nodes 834 | */ 835 | public function assertQueryContentRegex($path, $pattern) 836 | { 837 | $this->queryContentRegexAssertion($path, $pattern, false); 838 | } 839 | 840 | /** 841 | * Assert against XPath selection; node should match content 842 | * 843 | * @param string $path XPath path 844 | * @param string $pattern Pattern that should be contained in matched nodes 845 | */ 846 | public function assertXpathQueryContentRegex($path, $pattern) 847 | { 848 | $this->queryContentRegexAssertion($path, $pattern, true); 849 | } 850 | 851 | /** 852 | * Assert against DOM/XPath selection; node should NOT match content 853 | * 854 | * @param string $path CSS selector path 855 | * @param string $pattern pattern that should NOT be contained in matched nodes 856 | * @param bool $useXpath 857 | */ 858 | private function notQueryContentRegexAssertion($path, $pattern, $useXpath = false) 859 | { 860 | $result = $this->query($path, $useXpath); 861 | if ($result->count() == 0) { 862 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 863 | 'Failed asserting node DENOTED BY %s EXISTS', 864 | $path 865 | ))); 866 | } 867 | if (preg_match($pattern, $result->current()->nodeValue)) { 868 | throw new ExpectationFailedException($this->createFailureMessage(sprintf( 869 | 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content MATCHING "%s"', 870 | $path, 871 | $pattern 872 | ))); 873 | } 874 | $this->assertFalse((bool) preg_match($pattern, $result->current()->nodeValue)); 875 | } 876 | 877 | /** 878 | * Assert against DOM selection; node should NOT match content 879 | * 880 | * @param string $path CSS selector path 881 | * @param string $pattern pattern that should NOT be contained in matched nodes 882 | */ 883 | public function assertNotQueryContentRegex($path, $pattern) 884 | { 885 | $this->notQueryContentRegexAssertion($path, $pattern, false); 886 | } 887 | 888 | /** 889 | * Assert against XPath selection; node should NOT match content 890 | * 891 | * @param string $path XPath path 892 | * @param string $pattern pattern that should NOT be contained in matched nodes 893 | */ 894 | public function assertNotXpathQueryContentRegex($path, $pattern) 895 | { 896 | $this->notQueryContentRegexAssertion($path, $pattern, true); 897 | } 898 | } 899 | -------------------------------------------------------------------------------- /src/PHPUnit/TestCaseNoTypeHintTrait.php: -------------------------------------------------------------------------------- 1 | setUpCompat(); 14 | } 15 | } 16 | 17 | protected function tearDown() 18 | { 19 | if (method_exists($this, 'tearDownCompat')) { 20 | $this->tearDownCompat(); 21 | } 22 | } 23 | 24 | public static function setUpBeforeClass() 25 | { 26 | if (method_exists(static::class, 'setUpBeforeClassCompat')) { 27 | static::setUpBeforeClassCompat(); 28 | } 29 | } 30 | 31 | public static function tearDownAfterClass() 32 | { 33 | if (method_exists(static::class, 'tearDownAfterClassCompat')) { 34 | static::tearDownAfterClassCompat(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PHPUnit/TestCaseTypeHintTrait.php: -------------------------------------------------------------------------------- 1 | setUpCompat(); 14 | } 15 | } 16 | 17 | protected function tearDown() : void 18 | { 19 | if (method_exists($this, 'tearDownCompat')) { 20 | $this->tearDownCompat(); 21 | } 22 | } 23 | 24 | public static function setUpBeforeClass() : void 25 | { 26 | if (method_exists(static::class, 'setUpBeforeClassCompat')) { 27 | static::setUpBeforeClassCompat(); 28 | } 29 | } 30 | 31 | public static function tearDownAfterClass() : void 32 | { 33 | if (method_exists(static::class, 'tearDownAfterClassCompat')) { 34 | static::tearDownAfterClassCompat(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Util/ModuleLoader.php: -------------------------------------------------------------------------------- 1 | [ 32 | 'module_paths' => [], 33 | ], 34 | 'modules' => [ 35 | 'Zend\Router', 36 | 'Zend\Validator', 37 | ], 38 | ]; 39 | foreach ($modules as $key => $module) { 40 | if (is_numeric($key)) { 41 | $configuration['modules'][] = $module; 42 | continue; 43 | } 44 | $configuration['modules'][] = $key; 45 | $configuration['module_listener_options']['module_paths'][$key] = $module; 46 | } 47 | } 48 | 49 | $smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : []; 50 | $this->serviceManager = new ServiceManager(); 51 | (new Service\ServiceManagerConfig($smConfig))->configureServiceManager($this->serviceManager); 52 | $this->serviceManager->setService('ApplicationConfig', $configuration); 53 | $this->serviceManager->get('ModuleManager')->loadModules(); 54 | } 55 | 56 | /** 57 | * Get the application 58 | * 59 | * @return \Zend\Mvc\Application 60 | */ 61 | public function getApplication() 62 | { 63 | return $this->getServiceManager()->get('Application'); 64 | } 65 | 66 | /** 67 | * Get the module manager 68 | * 69 | * @return \Zend\ModuleManager\ModuleManager 70 | */ 71 | public function getModuleManager() 72 | { 73 | return $this->getServiceManager()->get('ModuleManager'); 74 | } 75 | 76 | /** 77 | * Get module by name 78 | * 79 | * @param $moduleName 80 | * @return mixed 81 | */ 82 | public function getModule($moduleName) 83 | { 84 | return $this->getModuleManager()->getModule($moduleName); 85 | } 86 | 87 | /** 88 | * Get the service manager 89 | * 90 | * @return ServiceManager 91 | */ 92 | public function getServiceManager() 93 | { 94 | return $this->serviceManager; 95 | } 96 | } 97 | --------------------------------------------------------------------------------