PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Codigo/sidep/lib/Cake/Test/Case/Error/ExceptionRendererTest.php

http://sidep.googlecode.com/
PHP | 738 lines | 434 code | 94 blank | 210 comment | 1 complexity | f1ef8ee4088a7319b7973c710855324c MD5 | raw file
  1. <?php
  2. /**
  3. * ExceptionRendererTest file
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
  8. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice
  12. *
  13. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
  15. * @package Cake.Test.Case.Error
  16. * @since CakePHP(tm) v 2.0
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('ExceptionRenderer', 'Error');
  20. App::uses('Controller', 'Controller');
  21. App::uses('AppController', 'Controller');
  22. App::uses('Component', 'Controller');
  23. App::uses('Router', 'Routing');
  24. /**
  25. * Short description for class.
  26. *
  27. * @package Cake.Test.Case.Error
  28. */
  29. class AuthBlueberryUser extends CakeTestModel {
  30. /**
  31. * name property
  32. *
  33. * @var string 'AuthBlueberryUser'
  34. */
  35. public $name = 'AuthBlueberryUser';
  36. /**
  37. * useTable property
  38. *
  39. * @var string
  40. */
  41. public $useTable = false;
  42. }
  43. /**
  44. * BlueberryComponent class
  45. *
  46. * @package Cake.Test.Case.Error
  47. */
  48. class BlueberryComponent extends Component {
  49. /**
  50. * testName property
  51. *
  52. * @return void
  53. */
  54. public $testName = null;
  55. /**
  56. * initialize method
  57. *
  58. * @return void
  59. */
  60. public function initialize(Controller $controller) {
  61. $this->testName = 'BlueberryComponent';
  62. }
  63. }
  64. /**
  65. * TestErrorController class
  66. *
  67. * @package Cake.Test.Case.Error
  68. */
  69. class TestErrorController extends Controller {
  70. /**
  71. * uses property
  72. *
  73. * @var array
  74. */
  75. public $uses = array();
  76. /**
  77. * components property
  78. *
  79. * @return void
  80. */
  81. public $components = array('Blueberry');
  82. /**
  83. * beforeRender method
  84. *
  85. * @return void
  86. */
  87. public function beforeRender() {
  88. echo $this->Blueberry->testName;
  89. }
  90. /**
  91. * index method
  92. *
  93. * @return void
  94. */
  95. public function index() {
  96. $this->autoRender = false;
  97. return 'what up';
  98. }
  99. }
  100. /**
  101. * MyCustomExceptionRenderer class
  102. *
  103. * @package Cake.Test.Case.Error
  104. */
  105. class MyCustomExceptionRenderer extends ExceptionRenderer {
  106. /**
  107. * custom error message type.
  108. *
  109. * @return void
  110. */
  111. public function missingWidgetThing() {
  112. echo 'widget thing is missing';
  113. }
  114. }
  115. /**
  116. * Exception class for testing app error handlers and custom errors.
  117. *
  118. * @package Cake.Test.Case.Error
  119. */
  120. class MissingWidgetThingException extends NotFoundException { }
  121. /**
  122. * ExceptionRendererTest class
  123. *
  124. * @package Cake.Test.Case.Error
  125. */
  126. class ExceptionRendererTest extends CakeTestCase {
  127. public $_restoreError = false;
  128. /**
  129. * setup create a request object to get out of router later.
  130. *
  131. * @return void
  132. */
  133. public function setUp() {
  134. parent::setUp();
  135. App::build(array(
  136. 'View' => array(
  137. CAKE . 'Test' . DS . 'test_app' . DS . 'View'. DS
  138. )
  139. ), App::RESET);
  140. Router::reload();
  141. $request = new CakeRequest(null, false);
  142. $request->base = '';
  143. Router::setRequestInfo($request);
  144. $this->_debug = Configure::read('debug');
  145. $this->_error = Configure::read('Error');
  146. Configure::write('debug', 2);
  147. }
  148. /**
  149. * tearDown
  150. *
  151. * @return void
  152. */
  153. public function tearDown() {
  154. Configure::write('debug', $this->_debug);
  155. Configure::write('Error', $this->_error);
  156. App::build();
  157. if ($this->_restoreError) {
  158. restore_error_handler();
  159. }
  160. parent::tearDown();
  161. }
  162. /**
  163. * Mocks out the response on the ExceptionRenderer object so headers aren't modified.
  164. *
  165. * @return void
  166. */
  167. protected function _mockResponse($error) {
  168. $error->controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
  169. return $error;
  170. }
  171. /**
  172. * test that methods declared in an ExceptionRenderer subclass are not converted
  173. * into error400 when debug > 0
  174. *
  175. * @return void
  176. */
  177. public function testSubclassMethodsNotBeingConvertedToError() {
  178. Configure::write('debug', 2);
  179. $exception = new MissingWidgetThingException('Widget not found');
  180. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  181. ob_start();
  182. $ExceptionRenderer->render();
  183. $result = ob_get_clean();
  184. $this->assertEquals($result, 'widget thing is missing');
  185. }
  186. /**
  187. * test that subclass methods are not converted when debug = 0
  188. *
  189. * @return void
  190. */
  191. public function testSubclassMethodsNotBeingConvertedDebug0() {
  192. Configure::write('debug', 0);
  193. $exception = new MissingWidgetThingException('Widget not found');
  194. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  195. $this->assertEquals('missingWidgetThing', $ExceptionRenderer->method);
  196. ob_start();
  197. $ExceptionRenderer->render();
  198. $result = ob_get_clean();
  199. $this->assertEquals($result, 'widget thing is missing', 'Method declared in subclass converted to error400');
  200. }
  201. /**
  202. * test that ExceptionRenderer subclasses properly convert framework errors.
  203. *
  204. * @return void
  205. */
  206. public function testSubclassConvertingFrameworkErrors() {
  207. Configure::write('debug', 0);
  208. $exception = new MissingControllerException('PostsController');
  209. $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
  210. $this->assertEquals('error400', $ExceptionRenderer->method);
  211. ob_start();
  212. $ExceptionRenderer->render();
  213. $result = ob_get_clean();
  214. $this->assertRegExp('/Not Found/', $result, 'Method declared in error handler not converted to error400. %s');
  215. }
  216. /**
  217. * test things in the constructor.
  218. *
  219. * @return void
  220. */
  221. public function testConstruction() {
  222. $exception = new NotFoundException('Page not found');
  223. $ExceptionRenderer = new ExceptionRenderer($exception);
  224. $this->assertInstanceOf('CakeErrorController', $ExceptionRenderer->controller);
  225. $this->assertEquals('error400', $ExceptionRenderer->method);
  226. $this->assertEquals($exception, $ExceptionRenderer->error);
  227. }
  228. /**
  229. * test that method gets coerced when debug = 0
  230. *
  231. * @return void
  232. */
  233. public function testErrorMethodCoercion() {
  234. Configure::write('debug', 0);
  235. $exception = new MissingActionException('Page not found');
  236. $ExceptionRenderer = new ExceptionRenderer($exception);
  237. $this->assertInstanceOf('CakeErrorController', $ExceptionRenderer->controller);
  238. $this->assertEquals('error400', $ExceptionRenderer->method);
  239. $this->assertEquals($exception, $ExceptionRenderer->error);
  240. }
  241. /**
  242. * test that unknown exception types with valid status codes are treated correctly.
  243. *
  244. * @return void
  245. */
  246. public function testUnknownExceptionTypeWithExceptionThatHasA400Code() {
  247. $exception = new MissingWidgetThingException('coding fail.');
  248. $ExceptionRenderer = new ExceptionRenderer($exception);
  249. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  250. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
  251. ob_start();
  252. $ExceptionRenderer->render();
  253. $result = ob_get_clean();
  254. $this->assertFalse(method_exists($ExceptionRenderer, 'missingWidgetThing'), 'no method should exist.');
  255. $this->assertEquals('error400', $ExceptionRenderer->method, 'incorrect method coercion.');
  256. $this->assertContains('coding fail', $result, 'Text should show up.');
  257. }
  258. /**
  259. * test that unknown exception types with valid status codes are treated correctly.
  260. *
  261. * @return void
  262. */
  263. public function testUnknownExceptionTypeWithNoCodeIsA500() {
  264. $exception = new OutOfBoundsException('foul ball.');
  265. $ExceptionRenderer = new ExceptionRenderer($exception);
  266. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  267. $ExceptionRenderer->controller->response->expects($this->once())
  268. ->method('statusCode')
  269. ->with(500);
  270. ob_start();
  271. $ExceptionRenderer->render();
  272. $result = ob_get_clean();
  273. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  274. $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
  275. }
  276. /**
  277. * test that unknown exceptions have messages ignored.
  278. *
  279. * @return void
  280. */
  281. public function testUnknownExceptionInProduction() {
  282. Configure::write('debug', 0);
  283. $exception = new OutOfBoundsException('foul ball.');
  284. $ExceptionRenderer = new ExceptionRenderer($exception);
  285. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  286. $ExceptionRenderer->controller->response->expects($this->once())
  287. ->method('statusCode')
  288. ->with(500);
  289. ob_start();
  290. $ExceptionRenderer->render();
  291. $result = ob_get_clean();
  292. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  293. $this->assertNotContains('foul ball.', $result, 'Text should no show up.');
  294. $this->assertContains('Internal Error', $result, 'Generic message only.');
  295. }
  296. /**
  297. * test that unknown exception types with valid status codes are treated correctly.
  298. *
  299. * @return void
  300. */
  301. public function testUnknownExceptionTypeWithCodeHigherThan500() {
  302. $exception = new OutOfBoundsException('foul ball.', 501);
  303. $ExceptionRenderer = new ExceptionRenderer($exception);
  304. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  305. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(501);
  306. ob_start();
  307. $ExceptionRenderer->render();
  308. $result = ob_get_clean();
  309. $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
  310. $this->assertContains('foul ball.', $result, 'Text should show up as its debug mode.');
  311. }
  312. /**
  313. * testerror400 method
  314. *
  315. * @return void
  316. */
  317. public function testError400() {
  318. Router::reload();
  319. $request = new CakeRequest('posts/view/1000', false);
  320. Router::setRequestInfo($request);
  321. $exception = new NotFoundException('Custom message');
  322. $ExceptionRenderer = new ExceptionRenderer($exception);
  323. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  324. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
  325. ob_start();
  326. $ExceptionRenderer->render();
  327. $result = ob_get_clean();
  328. $this->assertRegExp('/<h2>Custom message<\/h2>/', $result);
  329. $this->assertRegExp("/<strong>'.*?\/posts\/view\/1000'<\/strong>/", $result);
  330. }
  331. /**
  332. * test that error400 only modifies the messages on CakeExceptions.
  333. *
  334. * @return void
  335. */
  336. public function testerror400OnlyChangingCakeException() {
  337. Configure::write('debug', 0);
  338. $exception = new NotFoundException('Custom message');
  339. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  340. ob_start();
  341. $ExceptionRenderer->render();
  342. $result = ob_get_clean();
  343. $this->assertContains('Custom message', $result);
  344. $exception = new MissingActionException(array('controller' => 'PostsController', 'action' => 'index'));
  345. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  346. ob_start();
  347. $ExceptionRenderer->render();
  348. $result = ob_get_clean();
  349. $this->assertContains('Not Found', $result);
  350. }
  351. /**
  352. * test that error400 doesn't expose XSS
  353. *
  354. * @return void
  355. */
  356. public function testError400NoInjection() {
  357. Router::reload();
  358. $request = new CakeRequest('pages/<span id=333>pink</span></id><script>document.body.style.background = t=document.getElementById(333).innerHTML;window.alert(t);</script>', false);
  359. Router::setRequestInfo($request);
  360. $exception = new NotFoundException('Custom message');
  361. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  362. ob_start();
  363. $ExceptionRenderer->render();
  364. $result = ob_get_clean();
  365. $this->assertNotRegExp('#<script>document#', $result);
  366. $this->assertNotRegExp('#alert\(t\);</script>#', $result);
  367. }
  368. /**
  369. * testError500 method
  370. *
  371. * @return void
  372. */
  373. public function testError500Message() {
  374. $exception = new InternalErrorException('An Internal Error Has Occurred');
  375. $ExceptionRenderer = new ExceptionRenderer($exception);
  376. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  377. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
  378. ob_start();
  379. $ExceptionRenderer->render();
  380. $result = ob_get_clean();
  381. $this->assertRegExp('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
  382. }
  383. /**
  384. * testMissingController method
  385. *
  386. * @return void
  387. */
  388. public function testMissingController() {
  389. $exception = new MissingControllerException(array('class' => 'PostsController'));
  390. $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
  391. ob_start();
  392. $ExceptionRenderer->render();
  393. $result = ob_get_clean();
  394. $this->assertRegExp('/<h2>Missing Controller<\/h2>/', $result);
  395. $this->assertRegExp('/<em>PostsController<\/em>/', $result);
  396. }
  397. /**
  398. * Returns an array of tests to run for the various CakeException classes.
  399. *
  400. * @return void
  401. */
  402. public static function testProvider() {
  403. return array(
  404. array(
  405. new MissingActionException(array('controller' => 'PostsController', 'action' => 'index')),
  406. array(
  407. '/<h2>Missing Method in PostsController<\/h2>/',
  408. '/<em>PostsController::<\/em><em>index\(\)<\/em>/'
  409. ),
  410. 404
  411. ),
  412. array(
  413. new PrivateActionException(array('controller' => 'PostsController' , 'action' => '_secretSauce')),
  414. array(
  415. '/<h2>Private Method in PostsController<\/h2>/',
  416. '/<em>PostsController::<\/em><em>_secretSauce\(\)<\/em>/'
  417. ),
  418. 404
  419. ),
  420. array(
  421. new MissingTableException(array('table' => 'articles', 'class' => 'Article', 'ds' => 'test')),
  422. array(
  423. '/<h2>Missing Database Table<\/h2>/',
  424. '/Table <em>articles<\/em> for model <em>Article<\/em> was not found in datasource <em>test<\/em>/'
  425. ),
  426. 500
  427. ),
  428. array(
  429. new MissingDatabaseException(array('connection' => 'default')),
  430. array(
  431. '/<h2>Missing Database Connection<\/h2>/',
  432. '/Confirm you have created the file/'
  433. ),
  434. 500
  435. ),
  436. array(
  437. new MissingViewException(array('file' => '/posts/about.ctp')),
  438. array(
  439. "/posts\/about.ctp/"
  440. ),
  441. 500
  442. ),
  443. array(
  444. new MissingLayoutException(array('file' => 'layouts/my_layout.ctp')),
  445. array(
  446. "/Missing Layout/",
  447. "/layouts\/my_layout.ctp/"
  448. ),
  449. 500
  450. ),
  451. array(
  452. new MissingConnectionException(array('class' => 'Article')),
  453. array(
  454. '/<h2>Missing Database Connection<\/h2>/',
  455. '/Article requires a database connection/'
  456. ),
  457. 500
  458. ),
  459. array(
  460. new MissingDatasourceConfigException(array('config' => 'default')),
  461. array(
  462. '/<h2>Missing Datasource Configuration<\/h2>/',
  463. '/The datasource configuration <em>default<\/em> was not found in database.php/'
  464. ),
  465. 500
  466. ),
  467. array(
  468. new MissingDatasourceException(array('class' => 'MyDatasource', 'plugin' => 'MyPlugin')),
  469. array(
  470. '/<h2>Missing Datasource<\/h2>/',
  471. '/Datasource class <em>MyPlugin.MyDatasource<\/em> could not be found/'
  472. ),
  473. 500
  474. ),
  475. array(
  476. new MissingHelperException(array('class' => 'MyCustomHelper')),
  477. array(
  478. '/<h2>Missing Helper<\/h2>/',
  479. '/<em>MyCustomHelper<\/em> could not be found./',
  480. '/Create the class <em>MyCustomHelper<\/em> below in file:/',
  481. '/(\/|\\\)MyCustomHelper.php/'
  482. ),
  483. 500
  484. ),
  485. array(
  486. new MissingBehaviorException(array('class' => 'MyCustomBehavior')),
  487. array(
  488. '/<h2>Missing Behavior<\/h2>/',
  489. '/Create the class <em>MyCustomBehavior<\/em> below in file:/',
  490. '/(\/|\\\)MyCustomBehavior.php/'
  491. ),
  492. 500
  493. ),
  494. array(
  495. new MissingComponentException(array('class' => 'SideboxComponent')),
  496. array(
  497. '/<h2>Missing Component<\/h2>/',
  498. '/Create the class <em>SideboxComponent<\/em> below in file:/',
  499. '/(\/|\\\)SideboxComponent.php/'
  500. ),
  501. 500
  502. ),
  503. array(
  504. new Exception('boom'),
  505. array(
  506. '/Internal Error/'
  507. ),
  508. 500
  509. ),
  510. array(
  511. new RuntimeException('another boom'),
  512. array(
  513. '/Internal Error/'
  514. ),
  515. 500
  516. ),
  517. array(
  518. new CakeException('base class'),
  519. array('/Internal Error/'),
  520. 500
  521. ),
  522. array(
  523. new ConfigureException('No file'),
  524. array('/Internal Error/'),
  525. 500
  526. )
  527. );
  528. }
  529. /**
  530. * Test the various CakeException sub classes
  531. *
  532. * @dataProvider testProvider
  533. * @return void
  534. */
  535. public function testCakeExceptionHandling($exception, $patterns, $code) {
  536. $ExceptionRenderer = new ExceptionRenderer($exception);
  537. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  538. $ExceptionRenderer->controller->response->expects($this->once())
  539. ->method('statusCode')
  540. ->with($code);
  541. ob_start();
  542. $ExceptionRenderer->render();
  543. $result = ob_get_clean();
  544. foreach ($patterns as $pattern) {
  545. $this->assertRegExp($pattern, $result);
  546. }
  547. }
  548. /**
  549. * Test exceptions being raised when helpers are missing.
  550. *
  551. * @return void
  552. */
  553. public function testMissingRenderSafe() {
  554. $exception = new MissingHelperException(array('class' => 'Fail'));
  555. $ExceptionRenderer = new ExceptionRenderer($exception);
  556. $ExceptionRenderer->controller = $this->getMock('Controller');
  557. $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
  558. $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
  559. $ExceptionRenderer->controller->expects($this->at(2))
  560. ->method('render')
  561. ->with('missingHelper')
  562. ->will($this->throwException($exception));
  563. $ExceptionRenderer->controller->expects($this->at(4))
  564. ->method('render')
  565. ->with('error500')
  566. ->will($this->returnValue(true));
  567. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse');
  568. $ExceptionRenderer->render();
  569. sort($ExceptionRenderer->controller->helpers);
  570. $this->assertEquals(array('Form', 'Html', 'Session'), $ExceptionRenderer->controller->helpers);
  571. }
  572. /**
  573. * Test that missing subDir/layoutPath don't cause other fatal errors.
  574. *
  575. * @return void
  576. */
  577. public function testMissingSubdirRenderSafe() {
  578. $exception = new NotFoundException();
  579. $ExceptionRenderer = new ExceptionRenderer($exception);
  580. $ExceptionRenderer->controller = $this->getMock('Controller');
  581. $ExceptionRenderer->controller->helpers = array('Fail', 'Boom');
  582. $ExceptionRenderer->controller->layoutPath = 'json';
  583. $ExceptionRenderer->controller->subDir = 'json';
  584. $ExceptionRenderer->controller->viewClass = 'Json';
  585. $ExceptionRenderer->controller->request = $this->getMock('CakeRequest');
  586. $ExceptionRenderer->controller->expects($this->at(1))
  587. ->method('render')
  588. ->with('error400')
  589. ->will($this->throwException($exception));
  590. $ExceptionRenderer->controller->expects($this->at(3))
  591. ->method('render')
  592. ->with('error500')
  593. ->will($this->returnValue(true));
  594. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse');
  595. $ExceptionRenderer->controller->response->expects($this->once())
  596. ->method('type')
  597. ->with('html');
  598. $ExceptionRenderer->render();
  599. $this->assertEquals('', $ExceptionRenderer->controller->layoutPath);
  600. $this->assertEquals('', $ExceptionRenderer->controller->subDir);
  601. $this->assertEquals('View', $ExceptionRenderer->controller->viewClass);
  602. $this->assertEquals('Errors/', $ExceptionRenderer->controller->viewPath);
  603. }
  604. /**
  605. * Test that exceptions can be rendered when an request hasn't been registered
  606. * with Router
  607. *
  608. * @return void
  609. */
  610. public function testRenderWithNoRequest() {
  611. Router::reload();
  612. $this->assertNull(Router::getRequest(false));
  613. $exception = new Exception('Terrible');
  614. $ExceptionRenderer = new ExceptionRenderer($exception);
  615. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  616. $ExceptionRenderer->controller->response->expects($this->once())
  617. ->method('statusCode')
  618. ->with(500);
  619. ob_start();
  620. $ExceptionRenderer->render();
  621. $result = ob_get_clean();
  622. $this->assertContains('Internal Error', $result);
  623. }
  624. /**
  625. * Tests the output of rendering a PDOException
  626. *
  627. * @return void
  628. */
  629. public function testPDOException() {
  630. $exception = new PDOException('There was an error in the SQL query');
  631. $exception->queryString = 'SELECT * from poo_query < 5 and :seven';
  632. $exception->params = array('seven' => 7);
  633. $ExceptionRenderer = new ExceptionRenderer($exception);
  634. $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
  635. $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
  636. ob_start();
  637. $ExceptionRenderer->render();
  638. $result = ob_get_clean();
  639. $this->assertContains('<h2>Database Error</h2>', $result);
  640. $this->assertContains('There was an error in the SQL query', $result);
  641. $this->assertContains('SELECT * from poo_query < 5 and :seven', $result);
  642. $this->assertContains("'seven' => (int) 7", $result);
  643. }
  644. }