IntegrationTestCase.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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 = array(
  144. 'data' => $data,
  145. 'method' => $method,
  146. 'return' => 'vars'
  147. );
  148. $env = array();
  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 = array();
  157. foreach ($env as $k => $v) {
  158. $envBackup[$k] = isset($_ENV[$k]) ? $_ENV[$k] : null;
  159. $_ENV[$k] = $v;
  160. }
  161. $result = $this->testAction($url, $options);
  162. foreach ($env as $k => $v) {
  163. $_ENV[$k] = $envBackup[$k];
  164. }
  165. $this->_response = $this->controller->response;
  166. $this->_request = $this->controller->request;
  167. $this->_requestSession = $this->controller->Session;
  168. return $result;
  169. }
  170. /**
  171. * Fetch a view variable by name.
  172. *
  173. * If the view variable does not exist null will be returned.
  174. *
  175. * @param string $name The view variable to get.
  176. * @return mixed The view variable if set.
  177. */
  178. public function viewVariable($name) {
  179. if (empty($this->controller->viewVars)) {
  180. $this->fail('There are no view variables, perhaps you need to run a request?');
  181. }
  182. if (isset($this->controller->viewVars[$name])) {
  183. return $this->controller->viewVars[$name];
  184. }
  185. return null;
  186. }
  187. /**
  188. * Assert that the response status code is in the 2xx range.
  189. *
  190. * @return void
  191. */
  192. public function assertResponseOk() {
  193. $this->_assertStatus(200, 204, 'Status code is not between 200 and 204');
  194. }
  195. /**
  196. * Assert that the response status code is in the 4xx range.
  197. *
  198. * @return void
  199. */
  200. public function assertResponseError() {
  201. $this->_assertStatus(400, 417, 'Status code is not between 400 and 417');
  202. }
  203. /**
  204. * Assert that the response status code is in the 5xx range.
  205. *
  206. * @return void
  207. */
  208. public function assertResponseFailure() {
  209. $this->_assertStatus(500, 505, 'Status code is not between 500 and 505');
  210. }
  211. /**
  212. * Asserts a specific response status code.
  213. *
  214. * @param int $code Status code to assert.
  215. * @return void
  216. */
  217. public function assertResponseCode($code) {
  218. $actual = $this->_response->statusCode();
  219. $this->_assertStatus($code, $code, 'Status code is not ' . $code . ' but ' . $actual);
  220. }
  221. /**
  222. * Helper method for status assertions.
  223. *
  224. * @param int $min Min status code.
  225. * @param int $max Max status code.
  226. * @param string $message The error message.
  227. * @return void
  228. */
  229. protected function _assertStatus($min, $max, $message) {
  230. if (!$this->_response) {
  231. $this->fail('No response set, cannot assert status code.');
  232. }
  233. $status = $this->_response->statusCode();
  234. $this->assertGreaterThanOrEqual($min, $status, $message);
  235. $this->assertLessThanOrEqual($max, $status, $message);
  236. }
  237. /**
  238. * Assert that the Location header is correct.
  239. *
  240. * @param string|array $url The url you expected the client to go to. This
  241. * can either be a string URL or an array compatible with Router::url()
  242. * @param string $message The failure message that will be appended to the generated message.
  243. * @return void
  244. */
  245. public function assertRedirect($url, $message = '') {
  246. if (!$this->_response) {
  247. $this->fail('No response set, cannot assert location header. ' . $message);
  248. }
  249. $result = $this->_response->header();
  250. if (empty($result['Location'])) {
  251. $this->fail('No location header set. ' . $message);
  252. }
  253. $this->assertEquals(Router::url($url, ['_full' => true]), $result['Location'], $message);
  254. }
  255. /**
  256. * Assert response headers
  257. *
  258. * @param string $header The header to check
  259. * @param string $content The content to check for.
  260. * @param string $message The failure message that will be appended to the generated message.
  261. * @return void
  262. */
  263. public function assertHeader($header, $content, $message = '') {
  264. if (!$this->_response) {
  265. $this->fail('No response set, cannot assert headers. ' . $message);
  266. }
  267. $headers = $this->_response->header();
  268. if (!isset($headers[$header])) {
  269. $this->fail("The '$header' header is not set. " . $message);
  270. }
  271. $this->assertEquals($headers[$header], $content, $message);
  272. }
  273. /**
  274. * Assert content type
  275. *
  276. * @param string $type The content-type to check for.
  277. * @param string $message The failure message that will be appended to the generated message.
  278. * @return void
  279. */
  280. public function assertContentType($type, $message = '') {
  281. if (!$this->_response) {
  282. $this->fail('No response set, cannot assert content-type. ' . $message);
  283. }
  284. $alias = $this->_response->getMimeType($type);
  285. if ($alias !== false) {
  286. $type = $alias;
  287. }
  288. $result = $this->_response->type();
  289. $this->assertEquals($type, $result, $message);
  290. }
  291. /**
  292. * Assert content exists in the response body.
  293. *
  294. * @param string $content The content to check for.
  295. * @param string $message The failure message that will be appended to the generated message.
  296. * @return void
  297. */
  298. public function assertResponseContains($content, $message = '') {
  299. if (!$this->_response) {
  300. $this->fail('No response set, cannot assert content. ' . $message);
  301. }
  302. $this->assertContains($content, $this->_response->body(), $message);
  303. }
  304. /**
  305. * Assert that the search string was in the template name.
  306. *
  307. * @param string $content The content to check for.
  308. * @param string $message The failure message that will be appended to the generated message.
  309. * @return void
  310. */
  311. public function assertTemplate($content, $message = '') {
  312. if (!$this->_viewName) {
  313. $this->fail('No view name stored. ' . $message);
  314. }
  315. $this->assertContains($content, $this->_viewName, $message);
  316. }
  317. /**
  318. * Assert that the search string was in the layout name.
  319. *
  320. * @param string $content The content to check for.
  321. * @param string $message The failure message that will be appended to the generated message.
  322. * @return void
  323. */
  324. public function assertLayout($content, $message = '') {
  325. if (!$this->_layoutName) {
  326. $this->fail('No layout name stored. ' . $message);
  327. }
  328. $this->assertContains($content, $this->_layoutName, $message);
  329. }
  330. /**
  331. * Assert session contents
  332. *
  333. * @param string $expected The expected contents.
  334. * @param string $path The session data path. Uses Hash::get() compatible notation
  335. * @param string $message The failure message that will be appended to the generated message.
  336. * @return void
  337. */
  338. public function assertSession($expected, $path, $message = '') {
  339. if (empty($this->_requestSession)) {
  340. $this->fail('There is no stored session data. Perhaps you need to run a request?');
  341. }
  342. $result = $this->_requestSession->read($path);
  343. $this->assertEquals($expected, $result, 'Session content differs. ' . $message);
  344. }
  345. /**
  346. * Assert cookie values
  347. *
  348. * @param string $expected The expected contents.
  349. * @param string $name The cookie name.
  350. * @param string $message The failure message that will be appended to the generated message.
  351. * @return void
  352. */
  353. public function assertCookie($expected, $name, $message = '') {
  354. if (empty($this->_response)) {
  355. $this->fail('Not response set, cannot assert cookies.');
  356. }
  357. $result = $this->_response->cookie($name);
  358. $this->assertEquals($expected, $result['value'], 'Cookie data differs. ' . $message);
  359. }
  360. }