IntegrationTestCase.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. <?php
  2. App::uses('MyControllerTestCase', 'Tools.TestSuite');
  3. App::uses('Router', 'Routing');
  4. App::uses('Dispatcher', 'Routing');
  5. App::uses('EventManager', 'Event');
  6. /**
  7. * A test case class intended to make integration tests of
  8. * your controllers easier.
  9. *
  10. * This class has been backported from 3.0.
  11. * Does not support cookies or non 2xx/3xx responses yet, though.
  12. *
  13. * This test class provides a number of helper methods and features
  14. * that make dispatching requests and checking their responses simpler.
  15. * It favours full integration tests over mock objects as you can test
  16. * more of your code easily and avoid some of the maintenance pitfalls
  17. * that mock objects create.
  18. */
  19. abstract class IntegrationTestCase extends MyControllerTestCase {
  20. /**
  21. * The data used to build the next request.
  22. * Use the headers key to set specific $_ENV headers.
  23. *
  24. * @var array
  25. */
  26. protected $_requestData = [];
  27. /**
  28. * Session data to use in the next request.
  29. *
  30. * @var array
  31. */
  32. protected $_sessionData = [];
  33. /**
  34. * Configure the data for the *next* request.
  35. *
  36. * This data is cleared in the tearDown() method.
  37. *
  38. * You can call this method multiple times to append into
  39. * the current state.
  40. *
  41. * @param array $data The request data to use.
  42. * @return void
  43. */
  44. public function configRequest(array $data) {
  45. $this->_requestData = $data + $this->_requestData;
  46. }
  47. /**
  48. * Set session data.
  49. *
  50. * This method lets you configure the session data
  51. * you want to be used for requests that follow. The session
  52. * state is reset in each tearDown().
  53. *
  54. * You can call this method multiple times to append into
  55. * the current state.
  56. *
  57. * @param array $data The session data to use.
  58. * @return void
  59. */
  60. public function session(array $data) {
  61. $this->_sessionData = $data + $this->_sessionData;
  62. }
  63. /**
  64. * Perform a GET request using the current request data.
  65. *
  66. * The response of the dispatched request will be stored as
  67. * a property. You can use various assert methods to check the
  68. * response.
  69. *
  70. * @param string $url The url to request.
  71. * @return void
  72. */
  73. public function get($url) {
  74. return $this->_sendRequest($url, 'GET');
  75. }
  76. /**
  77. * Perform a POST request using the current request data.
  78. *
  79. * The response of the dispatched request will be stored as
  80. * a property. You can use various assert methods to check the
  81. * response.
  82. *
  83. * @param string $url The url to request.
  84. * @param array $data The data for the request.
  85. * @return void
  86. */
  87. public function post($url, $data = []) {
  88. return $this->_sendRequest($url, 'POST', $data);
  89. }
  90. /**
  91. * Perform a PATCH request using the current request data.
  92. *
  93. * The response of the dispatched request will be stored as
  94. * a property. You can use various assert methods to check the
  95. * response.
  96. *
  97. * @param string $url The url to request.
  98. * @param array $data The data for the request.
  99. * @return void
  100. */
  101. public function patch($url, $data = []) {
  102. return $this->_sendRequest($url, 'PATCH', $data);
  103. }
  104. /**
  105. * Perform a PUT request using the current request data.
  106. *
  107. * The response of the dispatched request will be stored as
  108. * a property. You can use various assert methods to check the
  109. * response.
  110. *
  111. * @param string $url The url to request.
  112. * @param array $data The data for the request.
  113. * @return void
  114. */
  115. public function put($url, $data = []) {
  116. return $this->_sendRequest($url, 'PUT', $data);
  117. }
  118. /**
  119. * Perform a DELETE request using the current request data.
  120. *
  121. * The response of the dispatched request will be stored as
  122. * a property. You can use various assert methods to check the
  123. * response.
  124. *
  125. * @param string $url The url to request.
  126. * @return void
  127. */
  128. public function delete($url) {
  129. return $this->_sendRequest($url, 'DELETE');
  130. }
  131. /**
  132. * Create and send the request into a Dispatcher instance.
  133. *
  134. * Receives and stores the response for future inspection.
  135. *
  136. * @param string $url The url
  137. * @param string $method The HTTP method
  138. * @param array|null $data The request data.
  139. * @return mixed
  140. * @throws \Exception
  141. */
  142. protected function _sendRequest($url, $method, $data = []) {
  143. $options = [
  144. 'data' => $data,
  145. 'method' => $method,
  146. 'return' => 'vars'
  147. ];
  148. $env = [];
  149. if (isset($this->_requestData['headers'])) {
  150. foreach ($this->_requestData['headers'] as $k => $v) {
  151. $env['HTTP_' . str_replace('-', '_', strtoupper($k))] = $v;
  152. }
  153. unset($this->_requestData['headers']);
  154. }
  155. CakeSession::write($this->_sessionData);
  156. $envBackup = $serverBackup = [];
  157. foreach ($env as $k => $v) {
  158. $envBackup[$k] = isset($_ENV[$k]) ? $_ENV[$k] : null;
  159. $serverBackup[$k] = isset($_ENV[$k]) ? $_ENV[$k] : null;
  160. $_ENV[$k] = $v;
  161. $_SERVER[$k] = $v;
  162. }
  163. $result = $this->testAction($url, $options);
  164. foreach ($env as $k => $v) {
  165. $_ENV[$k] = $envBackup[$k];
  166. $_SERVER[$k] = $serverBackup[$k];
  167. }
  168. $this->_response = $this->controller->response;
  169. $this->_request = $this->controller->request;
  170. $this->_requestSession = $this->controller->Session;
  171. return $result;
  172. }
  173. public function tearDown() {
  174. parent::tearDown();
  175. if ((float)Configure::version() >= 2.7) {
  176. CakeSession::clear(false);
  177. } else {
  178. $_SESSION = null;
  179. }
  180. }
  181. /**
  182. * Fetch a view variable by name.
  183. *
  184. * If the view variable does not exist null will be returned.
  185. *
  186. * @param string $name The view variable to get.
  187. * @return mixed The view variable if set.
  188. */
  189. public function viewVariable($name) {
  190. if (empty($this->controller->viewVars)) {
  191. $this->fail('There are no view variables, perhaps you need to run a request?');
  192. }
  193. if (isset($this->controller->viewVars[$name])) {
  194. return $this->controller->viewVars[$name];
  195. }
  196. return null;
  197. }
  198. /**
  199. * Assert that the response status code is in the 2xx range.
  200. *
  201. * @return void
  202. */
  203. public function assertResponseOk() {
  204. $this->_assertStatus(200, 204, 'Status code is not between 200 and 204');
  205. }
  206. /**
  207. * Assert that the response status code is in the 4xx range.
  208. *
  209. * @return void
  210. */
  211. public function assertResponseError() {
  212. $this->_assertStatus(400, 417, 'Status code is not between 400 and 417');
  213. }
  214. /**
  215. * Assert that the response status code is in the 5xx range.
  216. *
  217. * @return void
  218. */
  219. public function assertResponseFailure() {
  220. $this->_assertStatus(500, 505, 'Status code is not between 500 and 505');
  221. }
  222. /**
  223. * Asserts a specific response status code.
  224. *
  225. * @param int $code Status code to assert.
  226. * @return void
  227. */
  228. public function assertResponseCode($code) {
  229. $actual = $this->_response->statusCode();
  230. $this->_assertStatus($code, $code, 'Status code is not ' . $code . ' but ' . $actual);
  231. }
  232. /**
  233. * Helper method for status assertions.
  234. *
  235. * @param int $min Min status code.
  236. * @param int $max Max status code.
  237. * @param string $message The error message.
  238. * @return void
  239. */
  240. protected function _assertStatus($min, $max, $message) {
  241. if (!$this->_response) {
  242. $this->fail('No response set, cannot assert status code.');
  243. }
  244. $status = $this->_response->statusCode();
  245. $this->assertGreaterThanOrEqual($min, $status, $message);
  246. $this->assertLessThanOrEqual($max, $status, $message);
  247. }
  248. /**
  249. * Assert that the Location header is correct.
  250. *
  251. * @param string|array|null $url The URL you expected the client to go to. This
  252. * can either be a string URL or an array compatible with Router::url()
  253. * @param string $message The failure message that will be appended to the generated message.
  254. * @return void
  255. */
  256. public function assertRedirect($url = null, $message = '') {
  257. if (!$this->_response) {
  258. $this->fail('No response set, cannot assert location header. ' . $message);
  259. }
  260. $result = $this->_response->header();
  261. if ($url === null) {
  262. $this->assertTrue(!empty($result['Location']), $message);
  263. return;
  264. }
  265. if (empty($result['Location'])) {
  266. $this->fail('No location header set. ' . $message);
  267. }
  268. $this->assertEquals(Router::url($url, true), $result['Location'], $message);
  269. }
  270. /**
  271. * Asserts that the Location header is correct.
  272. *
  273. * @param string|array $url The url you expected the client to go to. This
  274. * can either be a string URL or an array compatible with Router::url()
  275. * @param string $message The failure message that will be appended to the generated message.
  276. * @return void
  277. */
  278. public function assertNoRedirect($message = '') {
  279. if (!$this->_response) {
  280. $this->fail('No response set, cannot assert location header. ' . $message);
  281. }
  282. $result = $this->_response->header();
  283. if (!$message) {
  284. $message = 'Redirect header set';
  285. }
  286. if (!empty($result['Location'])) {
  287. $message .= ': ' . $result['Location'];
  288. }
  289. $this->assertTrue(empty($result['Location']), $message);
  290. }
  291. /**
  292. * Assert response headers
  293. *
  294. * @param string $header The header to check
  295. * @param string $content The content to check for.
  296. * @param string $message The failure message that will be appended to the generated message.
  297. * @return void
  298. */
  299. public function assertHeader($header, $content, $message = '') {
  300. if (!$this->_response) {
  301. $this->fail('No response set, cannot assert headers. ' . $message);
  302. }
  303. $headers = $this->_response->header();
  304. if (!isset($headers[$header])) {
  305. $this->fail("The '$header' header is not set. " . $message);
  306. }
  307. $this->assertEquals($headers[$header], $content, $message);
  308. }
  309. /**
  310. * Assert content type
  311. *
  312. * @param string $type The content-type to check for.
  313. * @param string $message The failure message that will be appended to the generated message.
  314. * @return void
  315. */
  316. public function assertContentType($type, $message = '') {
  317. if (!$this->_response) {
  318. $this->fail('No response set, cannot assert content-type. ' . $message);
  319. }
  320. $alias = $this->_response->getMimeType($type);
  321. if ($alias !== false) {
  322. $type = $alias;
  323. }
  324. $result = $this->_response->type();
  325. $this->assertEquals($type, $result, $message);
  326. }
  327. /**
  328. * Assert content exists in the response body.
  329. *
  330. * @param string $content The content to check for.
  331. * @param string $message The failure message that will be appended to the generated message.
  332. * @return void
  333. */
  334. public function assertResponseEquals($content, $message = '') {
  335. if (!$this->_response) {
  336. $this->fail('No response set, cannot assert content. ' . $message);
  337. }
  338. $this->assertEquals($content, $this->_response->body(), $message);
  339. }
  340. /**
  341. * Assert content exists in the response body.
  342. *
  343. * @param string $content The content to check for.
  344. * @param string $message The failure message that will be appended to the generated message.
  345. * @return void
  346. */
  347. public function assertResponseContains($content, $message = '') {
  348. if (!$this->_response) {
  349. $this->fail('No response set, cannot assert content. ' . $message);
  350. }
  351. $this->assertContains($content, (string)$this->_response->body(), $message);
  352. }
  353. /**
  354. * Assert content does not exist in the response body.
  355. *
  356. * @param string $content The content to check for.
  357. * @param string $message The failure message that will be appended to the generated message.
  358. * @return void
  359. */
  360. public function assertResponseNotContains($content, $message = '') {
  361. if (!$this->_response) {
  362. $this->fail('No response set, cannot assert content. ' . $message);
  363. }
  364. $this->assertNotContains($content, (string)$this->_response->body(), $message);
  365. }
  366. /**
  367. * Assert that the search string was in the template name.
  368. *
  369. * @param string $content The content to check for.
  370. * @param string $message The failure message that will be appended to the generated message.
  371. * @return void
  372. */
  373. public function assertTemplate($content, $message = '') {
  374. if (!$this->_viewName) {
  375. $this->fail('No view name stored. ' . $message);
  376. }
  377. $this->assertContains($content, $this->_viewName, $message);
  378. }
  379. /**
  380. * Assert that the search string was in the layout name.
  381. *
  382. * @param string $content The content to check for.
  383. * @param string $message The failure message that will be appended to the generated message.
  384. * @return void
  385. */
  386. public function assertLayout($content, $message = '') {
  387. if (!$this->_layoutName) {
  388. $this->fail('No layout name stored. ' . $message);
  389. }
  390. $this->assertContains($content, $this->_layoutName, $message);
  391. }
  392. /**
  393. * Assert session contents
  394. *
  395. * @param string $expected The expected contents.
  396. * @param string $path The session data path. Uses Hash::get() compatible notation
  397. * @param string $message The failure message that will be appended to the generated message.
  398. * @return void
  399. */
  400. public function assertSession($expected, $path, $message = '') {
  401. if (empty($this->_requestSession)) {
  402. $this->fail('There is no stored session data. Perhaps you need to run a request?');
  403. }
  404. $result = $this->_requestSession->read($path);
  405. $this->assertEquals($expected, $result, 'Session content differs. ' . $message);
  406. }
  407. /**
  408. * Assert cookie values
  409. *
  410. * @param string $expected The expected contents.
  411. * @param string $name The cookie name.
  412. * @param string $message The failure message that will be appended to the generated message.
  413. * @return void
  414. */
  415. public function assertCookie($expected, $name, $message = '') {
  416. if (empty($this->_response)) {
  417. $this->fail('Not response set, cannot assert cookies.');
  418. }
  419. $result = $this->_response->cookie($name);
  420. $this->assertEquals($expected, $result['value'], 'Cookie data differs. ' . $message);
  421. }
  422. }