TestCaseTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP Project
  13. * @since 1.2.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\TestSuite;
  17. use Cake\Core\Configure;
  18. use Cake\Database\Connection;
  19. use Cake\Datasource\ConnectionManager;
  20. use Cake\Event\Event;
  21. use Cake\Event\EventList;
  22. use Cake\Event\EventManager;
  23. use Cake\ORM\Entity;
  24. use Cake\ORM\Table;
  25. use Cake\Routing\Exception\MissingRouteException;
  26. use Cake\Routing\Router;
  27. use Cake\Test\Fixture\FixturizedTestCase;
  28. use Cake\TestSuite\TestCase;
  29. use Exception;
  30. use PHPUnit\Framework\AssertionFailedError;
  31. use PHPUnit\Framework\Attributes\WithoutErrorHandler;
  32. use PHPUnit\Framework\TestStatus\Skipped;
  33. use PHPUnit\Framework\TestStatus\Success;
  34. use TestApp\Model\Entity\Tag;
  35. use TestApp\Model\Table\PostsTable;
  36. use TestApp\Model\Table\SecondaryPostsTable;
  37. use TestPlugin\Model\Entity\Author;
  38. use TestPlugin\Model\Table\AuthorsTable;
  39. use TestPlugin\Model\Table\TestPluginCommentsTable;
  40. use function Cake\Core\deprecationWarning;
  41. /**
  42. * TestCaseTest
  43. */
  44. class TestCaseTest extends TestCase
  45. {
  46. public function setUp(): void
  47. {
  48. parent::setUp();
  49. $this->clearPlugins();
  50. }
  51. /**
  52. * tests trying to assertEventFired without configuring an event list
  53. */
  54. public function testEventFiredMisconfiguredEventList(): void
  55. {
  56. $this->expectException(AssertionFailedError::class);
  57. $manager = EventManager::instance();
  58. $this->assertEventFired('my.event', $manager);
  59. }
  60. /**
  61. * tests trying to assertEventFired without configuring an event list
  62. */
  63. public function testEventFiredWithMisconfiguredEventList(): void
  64. {
  65. $this->expectException(AssertionFailedError::class);
  66. $manager = EventManager::instance();
  67. $this->assertEventFiredWith('my.event', 'some', 'data', $manager);
  68. }
  69. /**
  70. * tests assertEventFiredWith
  71. */
  72. public function testEventFiredWith(): void
  73. {
  74. $manager = EventManager::instance();
  75. $manager->setEventList(new EventList());
  76. $manager->trackEvents(true);
  77. $event = new Event('my.event', $this, [
  78. 'some' => 'data',
  79. ]);
  80. $manager->dispatch($event);
  81. $this->assertEventFiredWith('my.event', 'some', 'data');
  82. $manager = new EventManager();
  83. $manager->setEventList(new EventList());
  84. $manager->trackEvents(true);
  85. $event = new Event('my.event', $this, [
  86. 'other' => 'data',
  87. ]);
  88. $manager->dispatch($event);
  89. $this->assertEventFiredWith('my.event', 'other', 'data', $manager);
  90. }
  91. /**
  92. * tests assertEventFired
  93. */
  94. public function testEventFired(): void
  95. {
  96. $manager = EventManager::instance();
  97. $manager->setEventList(new EventList());
  98. $manager->trackEvents(true);
  99. $event = new Event('my.event');
  100. $manager->dispatch($event);
  101. $this->assertEventFired('my.event');
  102. $manager = new EventManager();
  103. $manager->setEventList(new EventList());
  104. $manager->trackEvents(true);
  105. $event = new Event('my.event');
  106. $manager->dispatch($event);
  107. $this->assertEventFired('my.event', $manager);
  108. }
  109. /**
  110. * testSkipIf
  111. */
  112. public function testSkipIf(): void
  113. {
  114. $test = new FixturizedTestCase('testSkipIfTrue');
  115. $test->run();
  116. $result = $test->status();
  117. $this->assertInstanceOf(Skipped::class, $result);
  118. $test = new FixturizedTestCase('testSkipIfFalse');
  119. $test->run();
  120. $result = $test->status();
  121. $this->assertInstanceOf(Success::class, $result);
  122. }
  123. /**
  124. * test withErrorReporting
  125. */
  126. public function testWithErrorReporting(): void
  127. {
  128. $errorLevel = error_reporting();
  129. $this->withErrorReporting(E_USER_WARNING, function (): void {
  130. $this->assertSame(E_USER_WARNING, error_reporting());
  131. });
  132. $this->assertSame($errorLevel, error_reporting());
  133. }
  134. /**
  135. * test withCaptureError
  136. */
  137. public function testCaptureError(): void
  138. {
  139. $error = $this->captureError(E_USER_WARNING, function (): void {
  140. trigger_error('Something bad', E_USER_WARNING);
  141. });
  142. $this->assertEquals('Something bad', $error->getMessage());
  143. $this->assertEqualsWithDelta(__LINE__, $error->getLine(), 10);
  144. $this->assertEquals(E_USER_WARNING, $error->getCode());
  145. $this->assertEquals(__FILE__, $error->getFile());
  146. }
  147. /**
  148. * test withCaptureError
  149. */
  150. public function testCaptureErrorNoError(): void
  151. {
  152. $this->expectException(AssertionFailedError::class);
  153. $this->captureError(E_USER_WARNING, function (): void {
  154. // nothing
  155. });
  156. }
  157. /**
  158. * test withErrorReporting with exceptions
  159. */
  160. public function testWithErrorReportingWithException(): void
  161. {
  162. $this->expectException(AssertionFailedError::class);
  163. $errorLevel = error_reporting();
  164. try {
  165. $this->withErrorReporting(E_USER_WARNING, function (): void {
  166. $this->assertSame(1, 2);
  167. });
  168. } finally {
  169. $this->assertSame($errorLevel, error_reporting());
  170. }
  171. }
  172. /**
  173. * test deprecated
  174. */
  175. public function testDeprecated(): void
  176. {
  177. $this->deprecated(function (): void {
  178. trigger_error('deprecation message', E_USER_DEPRECATED);
  179. });
  180. }
  181. /**
  182. * test deprecated with assert after trigger warning
  183. */
  184. public function testDeprecatedWithAssertAfterTriggerWarning(): void
  185. {
  186. try {
  187. $this->deprecated(function (): void {
  188. trigger_error('deprecation message', E_USER_DEPRECATED);
  189. $this->fail('A random message');
  190. });
  191. $this->fail();
  192. } catch (Exception $e) {
  193. $this->assertStringContainsString('A random message', $e->getMessage());
  194. }
  195. }
  196. /**
  197. * test deprecated
  198. */
  199. public function testDeprecatedWithNoDeprecation(): void
  200. {
  201. try {
  202. $this->deprecated(function (): void {
  203. });
  204. $this->fail();
  205. } catch (Exception $e) {
  206. $this->assertStringStartsWith('Should have at least one deprecation warning', $e->getMessage());
  207. }
  208. }
  209. /**
  210. * test deprecated() with duplicate deprecation with same messsage and line
  211. */
  212. #[WithoutErrorHandler]
  213. public function testDeprecatedWithDuplicatedDeprecation(): void
  214. {
  215. /**
  216. * setting stackframe = 0 and having same method
  217. * to have same deprecation message and same line for all cases
  218. */
  219. $fun = function (): void {
  220. deprecationWarning('5.0.0', 'Test same deprecation message', 0);
  221. };
  222. $this->deprecated(function () use ($fun): void {
  223. $fun();
  224. });
  225. $this->deprecated(function () use ($fun): void {
  226. $fun();
  227. });
  228. }
  229. /**
  230. * Test that TestCase::setUp() backs up values.
  231. */
  232. public function testSetupBackUpValues(): void
  233. {
  234. $this->assertArrayHasKey('debug', $this->_configure);
  235. }
  236. /**
  237. * test assertTextNotEquals()
  238. */
  239. public function testAssertTextNotEquals(): void
  240. {
  241. $one = "\r\nOne\rTwooo";
  242. $two = "\nOne\nTwo";
  243. $this->assertTextNotEquals($one, $two);
  244. }
  245. /**
  246. * test assertTextEquals()
  247. */
  248. public function testAssertTextEquals(): void
  249. {
  250. $one = "\r\nOne\rTwo";
  251. $two = "\nOne\nTwo";
  252. $this->assertTextEquals($one, $two);
  253. }
  254. /**
  255. * test assertTextStartsWith()
  256. */
  257. public function testAssertTextStartsWith(): void
  258. {
  259. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  260. $this->assertStringStartsWith("some\nstring", $stringDirty);
  261. $this->assertStringStartsNotWith("some\r\nstring\r\nwith", $stringDirty);
  262. $this->assertStringStartsNotWith("some\nstring\nwith", $stringDirty);
  263. $this->assertTextStartsWith("some\nstring\nwith", $stringDirty);
  264. $this->assertTextStartsWith("some\r\nstring\r\nwith", $stringDirty);
  265. }
  266. /**
  267. * test assertTextStartsNotWith()
  268. */
  269. public function testAssertTextStartsNotWith(): void
  270. {
  271. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  272. $this->assertTextStartsNotWith("some\nstring\nwithout", $stringDirty);
  273. }
  274. /**
  275. * test assertTextEndsWith()
  276. */
  277. public function testAssertTextEndsWith(): void
  278. {
  279. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  280. $this->assertTextEndsWith("string\nwith\r\ndifferent\rline endings!", $stringDirty);
  281. $this->assertTextEndsWith("string\r\nwith\ndifferent\nline endings!", $stringDirty);
  282. }
  283. /**
  284. * test assertTextEndsNotWith()
  285. */
  286. public function testAssertTextEndsNotWith(): void
  287. {
  288. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  289. $this->assertStringEndsNotWith("different\nline endings", $stringDirty);
  290. $this->assertTextEndsNotWith("different\rline endings", $stringDirty);
  291. }
  292. /**
  293. * test assertTextContains()
  294. */
  295. public function testAssertTextContains(): void
  296. {
  297. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  298. $this->assertStringContainsString('different', $stringDirty);
  299. $this->assertStringNotContainsString("different\rline", $stringDirty);
  300. $this->assertTextContains("different\rline", $stringDirty);
  301. }
  302. /**
  303. * test assertTextNotContains()
  304. */
  305. public function testAssertTextNotContains(): void
  306. {
  307. $stringDirty = "some\nstring\r\nwith\rdifferent\nline endings!";
  308. $this->assertTextNotContains("different\rlines", $stringDirty);
  309. }
  310. /**
  311. * test testAssertWithinRange()
  312. */
  313. public function testAssertWithinRange(): void
  314. {
  315. $this->assertWithinRange(21, 22, 1, 'Not within range');
  316. $this->assertWithinRange(21.3, 22.2, 1.0, 'Not within range');
  317. }
  318. /**
  319. * test testAssertNotWithinRange()
  320. */
  321. public function testAssertNotWithinRange(): void
  322. {
  323. $this->assertNotWithinRange(21, 23, 1, 'Within range');
  324. $this->assertNotWithinRange(21.3, 22.2, 0.7, 'Within range');
  325. }
  326. /**
  327. * test getMockForModel()
  328. */
  329. public function testGetMockForModel(): void
  330. {
  331. static::setAppNamespace();
  332. // No methods will be mocked if $methods argument of getMockForModel() is empty.
  333. $Posts = $this->getMockForModel('Posts');
  334. $entity = new Entity([]);
  335. $this->assertInstanceOf(PostsTable::class, $Posts);
  336. $this->assertSame('posts', $Posts->getTable());
  337. $Posts = $this->getMockForModel('Posts', ['save']);
  338. $Posts->expects($this->once())
  339. ->method('save')
  340. ->willReturn(false);
  341. $this->assertSame(false, $Posts->save($entity));
  342. $this->assertSame(Entity::class, $Posts->getEntityClass());
  343. $this->assertInstanceOf(Connection::class, $Posts->getConnection());
  344. $this->assertSame('test', $Posts->getConnection()->configName());
  345. $Tags = $this->getMockForModel('Tags', ['save']);
  346. $this->assertSame(Tag::class, $Tags->getEntityClass());
  347. $this->deprecated(function (): void {
  348. $SluggedPosts = $this->getMockForModel('SluggedPosts', ['slugify']);
  349. $SluggedPosts->expects($this->once())
  350. ->method('slugify')
  351. ->with('some value')
  352. ->willReturn('mocked');
  353. $this->assertSame('mocked', $SluggedPosts->slugify('some value'));
  354. $SluggedPosts = $this->getMockForModel('SluggedPosts', ['save', 'slugify']);
  355. $SluggedPosts->expects($this->once())
  356. ->method('slugify')
  357. ->with('some value two')
  358. ->willReturn('mocked');
  359. $this->assertSame('mocked', $SluggedPosts->slugify('some value two'));
  360. });
  361. }
  362. /**
  363. * Test getMockForModel on secondary datasources.
  364. */
  365. public function testGetMockForModelSecondaryDatasource(): void
  366. {
  367. ConnectionManager::alias('test', 'secondary');
  368. $post = $this->getMockForModel(SecondaryPostsTable::class, ['save']);
  369. $this->assertSame('test', $post->getConnection()->configName());
  370. }
  371. /**
  372. * test getMockForModel() with plugin models
  373. */
  374. public function testGetMockForModelWithPlugin(): void
  375. {
  376. static::setAppNamespace();
  377. $this->loadPlugins(['TestPlugin']);
  378. $TestPluginComment = $this->getMockForModel('TestPlugin.TestPluginComments');
  379. $result = $this->getTableLocator()->get('TestPlugin.TestPluginComments');
  380. $this->assertInstanceOf(TestPluginCommentsTable::class, $result);
  381. $this->assertSame($TestPluginComment, $result);
  382. $TestPluginComment = $this->getMockForModel('TestPlugin.TestPluginComments', ['save']);
  383. $this->assertInstanceOf(TestPluginCommentsTable::class, $TestPluginComment);
  384. $this->assertSame(Entity::class, $TestPluginComment->getEntityClass());
  385. $TestPluginComment->expects($this->exactly(1))
  386. ->method('save')
  387. ->willReturn(false);
  388. $entity = new Entity([]);
  389. $this->assertFalse($TestPluginComment->save($entity));
  390. $TestPluginAuthors = $this->getMockForModel('TestPlugin.Authors', ['save']);
  391. $this->assertInstanceOf(AuthorsTable::class, $TestPluginAuthors);
  392. $this->assertSame(Author::class, $TestPluginAuthors->getEntityClass());
  393. $this->clearPlugins();
  394. }
  395. /**
  396. * testGetMockForModelTable
  397. */
  398. public function testGetMockForModelTable(): void
  399. {
  400. $Mock = $this->getMockForModel(
  401. 'Table',
  402. ['save'],
  403. ['alias' => 'Comments', 'className' => Table::class]
  404. );
  405. $result = $this->getTableLocator()->get('Comments');
  406. $this->assertInstanceOf(Table::class, $result);
  407. $this->assertSame('Comments', $Mock->getAlias());
  408. $Mock->expects($this->exactly(1))
  409. ->method('save')
  410. ->willReturn(false);
  411. $entity = new Entity([]);
  412. $this->assertFalse($Mock->save($entity));
  413. $allMethodsStubs = $this->getMockForModel(
  414. 'Table',
  415. [],
  416. ['alias' => 'Comments', 'className' => Table::class]
  417. );
  418. $result = $this->getTableLocator()->get('Comments');
  419. $this->assertInstanceOf(Table::class, $result);
  420. $this->assertEmpty([], $allMethodsStubs->getAlias());
  421. }
  422. /**
  423. * Test getting a table mock that doesn't have a preset table name sets the proper name
  424. */
  425. public function testGetMockForModelSetTable(): void
  426. {
  427. static::setAppNamespace();
  428. ConnectionManager::alias('test', 'custom_i18n_datasource');
  429. $I18n = $this->getMockForModel('CustomI18n', ['save']);
  430. $this->assertSame('custom_i18n_table', $I18n->getTable());
  431. $Tags = $this->getMockForModel('Tags', ['save']);
  432. $this->assertSame('tags', $Tags->getTable());
  433. ConnectionManager::dropAlias('custom_i18n_datasource');
  434. }
  435. /**
  436. * Test loadRoutes() helper
  437. */
  438. public function testLoadRoutes(): void
  439. {
  440. $url = ['controller' => 'Articles', 'action' => 'index'];
  441. try {
  442. Router::url($url);
  443. $this->fail('Missing URL should throw an exception');
  444. } catch (MissingRouteException) {
  445. }
  446. Configure::write('App.namespace', 'TestApp');
  447. $this->loadRoutes();
  448. $result = Router::url($url);
  449. $this->assertSame('/app/articles', $result);
  450. }
  451. }