1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @since 1.2.0
12: * @license https://opensource.org/licenses/mit-license.php MIT License
13: */
14: namespace Cake\TestSuite;
15:
16: use Cake\Core\App;
17: use Cake\Core\Configure;
18: use Cake\Core\Plugin;
19: use Cake\Datasource\ConnectionManager;
20: use Cake\Event\EventManager;
21: use Cake\Http\BaseApplication;
22: use Cake\ORM\Entity;
23: use Cake\ORM\Exception\MissingTableClassException;
24: use Cake\ORM\Locator\LocatorAwareTrait;
25: use Cake\Routing\Router;
26: use Cake\TestSuite\Constraint\EventFired;
27: use Cake\TestSuite\Constraint\EventFiredWith;
28: use Cake\Utility\Inflector;
29: use Exception;
30: use PHPUnit\Framework\TestCase as BaseTestCase;
31:
32: /**
33: * Cake TestCase class
34: */
35: abstract class TestCase extends BaseTestCase
36: {
37:
38: use LocatorAwareTrait;
39:
40: /**
41: * The class responsible for managing the creation, loading and removing of fixtures
42: *
43: * @var \Cake\TestSuite\Fixture\FixtureManager|null
44: */
45: public $fixtureManager;
46:
47: /**
48: * By default, all fixtures attached to this class will be truncated and reloaded after each test.
49: * Set this to false to handle manually
50: *
51: * @var bool
52: */
53: public $autoFixtures = true;
54:
55: /**
56: * Control table create/drops on each test method.
57: *
58: * If true, tables will still be dropped at the
59: * end of each test runner execution.
60: *
61: * @var bool
62: */
63: public $dropTables = false;
64:
65: /**
66: * Configure values to restore at end of test.
67: *
68: * @var array
69: */
70: protected $_configure = [];
71:
72: /**
73: * Path settings to restore at the end of the test.
74: *
75: * @var array
76: */
77: protected $_pathRestore = [];
78:
79: /**
80: * Overrides SimpleTestCase::skipIf to provide a boolean return value
81: *
82: * @param bool $shouldSkip Whether or not the test should be skipped.
83: * @param string $message The message to display.
84: * @return bool
85: */
86: public function skipIf($shouldSkip, $message = '')
87: {
88: if ($shouldSkip) {
89: $this->markTestSkipped($message);
90: }
91:
92: return $shouldSkip;
93: }
94:
95: /**
96: * Helper method for tests that needs to use error_reporting()
97: *
98: * @param int $errorLevel value of error_reporting() that needs to use
99: * @param callable $callable callable function that will receive asserts
100: * @return void
101: */
102: public function withErrorReporting($errorLevel, $callable)
103: {
104: $default = error_reporting();
105: error_reporting($errorLevel);
106: try {
107: $callable();
108: } finally {
109: error_reporting($default);
110: }
111: }
112:
113: /**
114: * Helper method for check deprecation methods
115: *
116: * @param callable $callable callable function that will receive asserts
117: * @return void
118: */
119: public function deprecated($callable)
120: {
121: $errorLevel = error_reporting();
122: error_reporting(E_ALL ^ E_USER_DEPRECATED);
123: try {
124: $callable();
125: } finally {
126: error_reporting($errorLevel);
127: }
128: }
129:
130: /**
131: * Setup the test case, backup the static object values so they can be restored.
132: * Specifically backs up the contents of Configure and paths in App if they have
133: * not already been backed up.
134: *
135: * @return void
136: */
137: public function setUp()
138: {
139: parent::setUp();
140:
141: if (!$this->_configure) {
142: $this->_configure = Configure::read();
143: }
144: if (class_exists('Cake\Routing\Router', false)) {
145: Router::reload();
146: }
147:
148: EventManager::instance(new EventManager());
149: }
150:
151: /**
152: * teardown any static object changes and restore them.
153: *
154: * @return void
155: */
156: public function tearDown()
157: {
158: parent::tearDown();
159: if ($this->_configure) {
160: Configure::clear();
161: Configure::write($this->_configure);
162: }
163: $this->getTableLocator()->clear();
164: }
165:
166: /**
167: * Chooses which fixtures to load for a given test
168: *
169: * Each parameter is a model name that corresponds to a fixture, i.e. 'Posts', 'Authors', etc.
170: * Passing no parameters will cause all fixtures on the test case to load.
171: *
172: * @return void
173: * @see \Cake\TestSuite\TestCase::$autoFixtures
174: * @throws \Exception when no fixture manager is available.
175: */
176: public function loadFixtures()
177: {
178: if ($this->fixtureManager === null) {
179: throw new Exception('No fixture manager to load the test fixture');
180: }
181: $args = func_get_args();
182: foreach ($args as $class) {
183: $this->fixtureManager->loadSingle($class, null, $this->dropTables);
184: }
185:
186: if (empty($args)) {
187: $autoFixtures = $this->autoFixtures;
188: $this->autoFixtures = true;
189: $this->fixtureManager->load($this);
190: $this->autoFixtures = $autoFixtures;
191: }
192: }
193:
194: /**
195: * Load plugins into a simulated application.
196: *
197: * Useful to test how plugins being loaded/not loaded interact with other
198: * elements in CakePHP or applications.
199: *
200: * @param array $plugins List of Plugins to load.
201: * @return \Cake\Http\BaseApplication
202: */
203: public function loadPlugins(array $plugins = [])
204: {
205: /** @var \Cake\Http\BaseApplication $app */
206: $app = $this->getMockForAbstractClass(
207: BaseApplication::class,
208: ['']
209: );
210:
211: foreach ($plugins as $pluginName => $config) {
212: if (is_array($config)) {
213: $app->addPlugin($pluginName, $config);
214: } else {
215: $app->addPlugin($config);
216: }
217: }
218: $app->pluginBootstrap();
219: $builder = Router::createRouteBuilder('/');
220: $app->pluginRoutes($builder);
221:
222: return $app;
223: }
224:
225: /**
226: * Remove plugins from the global plugin collection.
227: *
228: * Useful in test case teardown methods.
229: *
230: * @param array $plugins A list of plugins you want to remove.
231: * @return void
232: */
233: public function removePlugins(array $plugins = [])
234: {
235: $collection = Plugin::getCollection();
236: foreach ($plugins as $plugin) {
237: $collection->remove($plugin);
238: }
239: }
240:
241: /**
242: * Clear all plugins from the global plugin collection.
243: *
244: * Useful in test case teardown methods.
245: *
246: * @return void
247: */
248: public function clearPlugins()
249: {
250: Plugin::getCollection()->clear();
251: }
252:
253: /**
254: * Asserts that a global event was fired. You must track events in your event manager for this assertion to work
255: *
256: * @param string $name Event name
257: * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
258: * @param string $message Assertion failure message
259: * @return void
260: */
261: public function assertEventFired($name, $eventManager = null, $message = '')
262: {
263: if (!$eventManager) {
264: $eventManager = EventManager::instance();
265: }
266: $this->assertThat($name, new EventFired($eventManager), $message);
267: }
268:
269: /**
270: * Asserts an event was fired with data
271: *
272: * If a third argument is passed, that value is used to compare with the value in $dataKey
273: *
274: * @param string $name Event name
275: * @param string $dataKey Data key
276: * @param string $dataValue Data value
277: * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
278: * @param string $message Assertion failure message
279: * @return void
280: */
281: public function assertEventFiredWith($name, $dataKey, $dataValue, $eventManager = null, $message = '')
282: {
283: if (!$eventManager) {
284: $eventManager = EventManager::instance();
285: }
286: $this->assertThat($name, new EventFiredWith($eventManager, $dataKey, $dataValue), $message);
287: }
288:
289: /**
290: * Assert text equality, ignoring differences in newlines.
291: * Helpful for doing cross platform tests of blocks of text.
292: *
293: * @param string $expected The expected value.
294: * @param string $result The actual value.
295: * @param string $message The message to use for failure.
296: * @return void
297: */
298: public function assertTextNotEquals($expected, $result, $message = '')
299: {
300: $expected = str_replace(["\r\n", "\r"], "\n", $expected);
301: $result = str_replace(["\r\n", "\r"], "\n", $result);
302: $this->assertNotEquals($expected, $result, $message);
303: }
304:
305: /**
306: * Assert text equality, ignoring differences in newlines.
307: * Helpful for doing cross platform tests of blocks of text.
308: *
309: * @param string $expected The expected value.
310: * @param string $result The actual value.
311: * @param string $message The message to use for failure.
312: * @return void
313: */
314: public function assertTextEquals($expected, $result, $message = '')
315: {
316: $expected = str_replace(["\r\n", "\r"], "\n", $expected);
317: $result = str_replace(["\r\n", "\r"], "\n", $result);
318: $this->assertEquals($expected, $result, $message);
319: }
320:
321: /**
322: * Asserts that a string starts with a given prefix, ignoring differences in newlines.
323: * Helpful for doing cross platform tests of blocks of text.
324: *
325: * @param string $prefix The prefix to check for.
326: * @param string $string The string to search in.
327: * @param string $message The message to use for failure.
328: * @return void
329: */
330: public function assertTextStartsWith($prefix, $string, $message = '')
331: {
332: $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
333: $string = str_replace(["\r\n", "\r"], "\n", $string);
334: $this->assertStringStartsWith($prefix, $string, $message);
335: }
336:
337: /**
338: * Asserts that a string starts not with a given prefix, ignoring differences in newlines.
339: * Helpful for doing cross platform tests of blocks of text.
340: *
341: * @param string $prefix The prefix to not find.
342: * @param string $string The string to search.
343: * @param string $message The message to use for failure.
344: * @return void
345: */
346: public function assertTextStartsNotWith($prefix, $string, $message = '')
347: {
348: $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
349: $string = str_replace(["\r\n", "\r"], "\n", $string);
350: $this->assertStringStartsNotWith($prefix, $string, $message);
351: }
352:
353: /**
354: * Asserts that a string ends with a given prefix, ignoring differences in newlines.
355: * Helpful for doing cross platform tests of blocks of text.
356: *
357: * @param string $suffix The suffix to find.
358: * @param string $string The string to search.
359: * @param string $message The message to use for failure.
360: * @return void
361: */
362: public function assertTextEndsWith($suffix, $string, $message = '')
363: {
364: $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
365: $string = str_replace(["\r\n", "\r"], "\n", $string);
366: $this->assertStringEndsWith($suffix, $string, $message);
367: }
368:
369: /**
370: * Asserts that a string ends not with a given prefix, ignoring differences in newlines.
371: * Helpful for doing cross platform tests of blocks of text.
372: *
373: * @param string $suffix The suffix to not find.
374: * @param string $string The string to search.
375: * @param string $message The message to use for failure.
376: * @return void
377: */
378: public function assertTextEndsNotWith($suffix, $string, $message = '')
379: {
380: $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
381: $string = str_replace(["\r\n", "\r"], "\n", $string);
382: $this->assertStringEndsNotWith($suffix, $string, $message);
383: }
384:
385: /**
386: * Assert that a string contains another string, ignoring differences in newlines.
387: * Helpful for doing cross platform tests of blocks of text.
388: *
389: * @param string $needle The string to search for.
390: * @param string $haystack The string to search through.
391: * @param string $message The message to display on failure.
392: * @param bool $ignoreCase Whether or not the search should be case-sensitive.
393: * @return void
394: */
395: public function assertTextContains($needle, $haystack, $message = '', $ignoreCase = false)
396: {
397: $needle = str_replace(["\r\n", "\r"], "\n", $needle);
398: $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
399: $this->assertContains($needle, $haystack, $message, $ignoreCase);
400: }
401:
402: /**
403: * Assert that a text doesn't contain another text, ignoring differences in newlines.
404: * Helpful for doing cross platform tests of blocks of text.
405: *
406: * @param string $needle The string to search for.
407: * @param string $haystack The string to search through.
408: * @param string $message The message to display on failure.
409: * @param bool $ignoreCase Whether or not the search should be case-sensitive.
410: * @return void
411: */
412: public function assertTextNotContains($needle, $haystack, $message = '', $ignoreCase = false)
413: {
414: $needle = str_replace(["\r\n", "\r"], "\n", $needle);
415: $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
416: $this->assertNotContains($needle, $haystack, $message, $ignoreCase);
417: }
418:
419: /**
420: * Asserts HTML tags.
421: *
422: * @param string $string An HTML/XHTML/XML string
423: * @param array $expected An array, see above
424: * @param bool $fullDebug Whether or not more verbose output should be used.
425: * @return void
426: * @deprecated 3.0. Use assertHtml() instead.
427: */
428: public function assertTags($string, $expected, $fullDebug = false)
429: {
430: deprecationWarning('TestCase::assertTags() is deprecated. Use TestCase::assertHtml() instead.');
431: $this->assertHtml($expected, $string, $fullDebug);
432: }
433:
434: /**
435: * Asserts HTML tags.
436: *
437: * Takes an array $expected and generates a regex from it to match the provided $string.
438: * Samples for $expected:
439: *
440: * Checks for an input tag with a name attribute (contains any non-empty value) and an id
441: * attribute that contains 'my-input':
442: *
443: * ```
444: * ['input' => ['name', 'id' => 'my-input']]
445: * ```
446: *
447: * Checks for two p elements with some text in them:
448: *
449: * ```
450: * [
451: * ['p' => true],
452: * 'textA',
453: * '/p',
454: * ['p' => true],
455: * 'textB',
456: * '/p'
457: * ]
458: * ```
459: *
460: * You can also specify a pattern expression as part of the attribute values, or the tag
461: * being defined, if you prepend the value with preg: and enclose it with slashes, like so:
462: *
463: * ```
464: * [
465: * ['input' => ['name', 'id' => 'preg:/FieldName\d+/']],
466: * 'preg:/My\s+field/'
467: * ]
468: * ```
469: *
470: * Important: This function is very forgiving about whitespace and also accepts any
471: * permutation of attribute order. It will also allow whitespace between specified tags.
472: *
473: * @param array $expected An array, see above
474: * @param string $string An HTML/XHTML/XML string
475: * @param bool $fullDebug Whether or not more verbose output should be used.
476: * @return bool
477: */
478: public function assertHtml($expected, $string, $fullDebug = false)
479: {
480: $regex = [];
481: $normalized = [];
482: foreach ((array)$expected as $key => $val) {
483: if (!is_numeric($key)) {
484: $normalized[] = [$key => $val];
485: } else {
486: $normalized[] = $val;
487: }
488: }
489: $i = 0;
490: foreach ($normalized as $tags) {
491: if (!is_array($tags)) {
492: $tags = (string)$tags;
493: }
494: $i++;
495: if (is_string($tags) && $tags{0} === '<') {
496: $tags = [substr($tags, 1) => []];
497: } elseif (is_string($tags)) {
498: $tagsTrimmed = preg_replace('/\s+/m', '', $tags);
499:
500: if (preg_match('/^\*?\//', $tags, $match) && $tagsTrimmed !== '//') {
501: $prefix = [null, null];
502:
503: if ($match[0] === '*/') {
504: $prefix = ['Anything, ', '.*?'];
505: }
506: $regex[] = [
507: sprintf('%sClose %s tag', $prefix[0], substr($tags, strlen($match[0]))),
508: sprintf('%s\s*<[\s]*\/[\s]*%s[\s]*>[\n\r]*', $prefix[1], substr($tags, strlen($match[0]))),
509: $i,
510: ];
511: continue;
512: }
513: if (!empty($tags) && preg_match('/^preg\:\/(.+)\/$/i', $tags, $matches)) {
514: $tags = $matches[1];
515: $type = 'Regex matches';
516: } else {
517: $tags = '\s*' . preg_quote($tags, '/');
518: $type = 'Text equals';
519: }
520: $regex[] = [
521: sprintf('%s "%s"', $type, $tags),
522: $tags,
523: $i,
524: ];
525: continue;
526: }
527: foreach ($tags as $tag => $attributes) {
528: $regex[] = [
529: sprintf('Open %s tag', $tag),
530: sprintf('[\s]*<%s', preg_quote($tag, '/')),
531: $i,
532: ];
533: if ($attributes === true) {
534: $attributes = [];
535: }
536: $attrs = [];
537: $explanations = [];
538: $i = 1;
539: foreach ($attributes as $attr => $val) {
540: if (is_numeric($attr) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
541: $attrs[] = $matches[1];
542: $explanations[] = sprintf('Regex "%s" matches', $matches[1]);
543: continue;
544: }
545:
546: $quotes = '["\']';
547: if (is_numeric($attr)) {
548: $attr = $val;
549: $val = '.+?';
550: $explanations[] = sprintf('Attribute "%s" present', $attr);
551: } elseif (!empty($val) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
552: $val = str_replace(
553: ['.*', '.+'],
554: ['.*?', '.+?'],
555: $matches[1]
556: );
557: $quotes = $val !== $matches[1] ? '["\']' : '["\']?';
558:
559: $explanations[] = sprintf('Attribute "%s" matches "%s"', $attr, $val);
560: } else {
561: $explanations[] = sprintf('Attribute "%s" == "%s"', $attr, $val);
562: $val = preg_quote($val, '/');
563: }
564: $attrs[] = '[\s]+' . preg_quote($attr, '/') . '=' . $quotes . $val . $quotes;
565: $i++;
566: }
567: if ($attrs) {
568: $regex[] = [
569: 'explains' => $explanations,
570: 'attrs' => $attrs,
571: ];
572: }
573: $regex[] = [
574: sprintf('End %s tag', $tag),
575: '[\s]*\/?[\s]*>[\n\r]*',
576: $i,
577: ];
578: }
579: }
580: foreach ($regex as $i => $assertion) {
581: $matches = false;
582: if (isset($assertion['attrs'])) {
583: $string = $this->_assertAttributes($assertion, $string, $fullDebug, $regex);
584: if ($fullDebug === true && $string === false) {
585: debug($string, true);
586: debug($regex, true);
587: }
588: continue;
589: }
590:
591: list($description, $expressions, $itemNum) = $assertion;
592: $expression = null;
593: foreach ((array)$expressions as $expression) {
594: $expression = sprintf('/^%s/s', $expression);
595: if (preg_match($expression, $string, $match)) {
596: $matches = true;
597: $string = substr($string, strlen($match[0]));
598: break;
599: }
600: }
601: if (!$matches) {
602: if ($fullDebug === true) {
603: debug($string);
604: debug($regex);
605: }
606: $this->assertRegExp($expression, $string, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
607:
608: return false;
609: }
610: }
611:
612: $this->assertTrue(true, '%s');
613:
614: return true;
615: }
616:
617: /**
618: * Check the attributes as part of an assertTags() check.
619: *
620: * @param array $assertions Assertions to run.
621: * @param string $string The HTML string to check.
622: * @param bool $fullDebug Whether or not more verbose output should be used.
623: * @param array|string $regex Full regexp from `assertHtml`
624: * @return string|bool
625: */
626: protected function _assertAttributes($assertions, $string, $fullDebug = false, $regex = '')
627: {
628: $asserts = $assertions['attrs'];
629: $explains = $assertions['explains'];
630: do {
631: $matches = false;
632: $j = null;
633: foreach ($asserts as $j => $assert) {
634: if (preg_match(sprintf('/^%s/s', $assert), $string, $match)) {
635: $matches = true;
636: $string = substr($string, strlen($match[0]));
637: array_splice($asserts, $j, 1);
638: array_splice($explains, $j, 1);
639: break;
640: }
641: }
642: if ($matches === false) {
643: if ($fullDebug === true) {
644: debug($string);
645: debug($regex);
646: }
647: $this->assertTrue(false, 'Attribute did not match. Was expecting ' . $explains[$j]);
648: }
649: $len = count($asserts);
650: } while ($len > 0);
651:
652: return $string;
653: }
654:
655: /**
656: * Normalize a path for comparison.
657: *
658: * @param string $path Path separated by "/" slash.
659: * @return string Normalized path separated by DIRECTORY_SEPARATOR.
660: */
661: protected function _normalizePath($path)
662: {
663: return str_replace('/', DIRECTORY_SEPARATOR, $path);
664: }
665:
666: // @codingStandardsIgnoreStart
667:
668: /**
669: * Compatibility function to test if a value is between an acceptable range.
670: *
671: * @param float $expected
672: * @param float $result
673: * @param float $margin the rage of acceptation
674: * @param string $message the text to display if the assertion is not correct
675: * @return void
676: */
677: protected static function assertWithinRange($expected, $result, $margin, $message = '')
678: {
679: $upper = $result + $margin;
680: $lower = $result - $margin;
681: static::assertTrue(($expected <= $upper) && ($expected >= $lower), $message);
682: }
683:
684: /**
685: * Compatibility function to test if a value is not between an acceptable range.
686: *
687: * @param float $expected
688: * @param float $result
689: * @param float $margin the rage of acceptation
690: * @param string $message the text to display if the assertion is not correct
691: * @return void
692: */
693: protected static function assertNotWithinRange($expected, $result, $margin, $message = '')
694: {
695: $upper = $result + $margin;
696: $lower = $result - $margin;
697: static::assertTrue(($expected > $upper) || ($expected < $lower), $message);
698: }
699:
700: /**
701: * Compatibility function to test paths.
702: *
703: * @param string $expected
704: * @param string $result
705: * @param string $message the text to display if the assertion is not correct
706: * @return void
707: */
708: protected static function assertPathEquals($expected, $result, $message = '')
709: {
710: $expected = str_replace(DIRECTORY_SEPARATOR, '/', $expected);
711: $result = str_replace(DIRECTORY_SEPARATOR, '/', $result);
712: static::assertEquals($expected, $result, $message);
713: }
714:
715: /**
716: * Compatibility function for skipping.
717: *
718: * @param bool $condition Condition to trigger skipping
719: * @param string $message Message for skip
720: * @return bool
721: */
722: protected function skipUnless($condition, $message = '')
723: {
724: if (!$condition) {
725: $this->markTestSkipped($message);
726: }
727:
728: return $condition;
729: }
730:
731: // @codingStandardsIgnoreEnd
732:
733: /**
734: * Mock a model, maintain fixtures and table association
735: *
736: * @param string $alias The model to get a mock for.
737: * @param array|null $methods The list of methods to mock
738: * @param array $options The config data for the mock's constructor.
739: * @throws \Cake\ORM\Exception\MissingTableClassException
740: * @return \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject
741: */
742: public function getMockForModel($alias, $methods = [], array $options = [])
743: {
744: /** @var \Cake\ORM\Table $className */
745: $className = $this->_getTableClassName($alias, $options);
746: $connectionName = $className::defaultConnectionName();
747: $connection = ConnectionManager::get($connectionName);
748:
749: $locator = $this->getTableLocator();
750:
751: list(, $baseClass) = pluginSplit($alias);
752: $options += ['alias' => $baseClass, 'connection' => $connection];
753: $options += $locator->getConfig($alias);
754:
755: /** @var \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject $mock */
756: $mock = $this->getMockBuilder($className)
757: ->setMethods($methods)
758: ->setConstructorArgs([$options])
759: ->getMock();
760:
761: if (empty($options['entityClass']) && $mock->getEntityClass() === Entity::class) {
762: $parts = explode('\\', $className);
763: $entityAlias = Inflector::classify(Inflector::underscore(substr(array_pop($parts), 0, -5)));
764: $entityClass = implode('\\', array_slice($parts, 0, -1)) . '\\Entity\\' . $entityAlias;
765: if (class_exists($entityClass)) {
766: $mock->setEntityClass($entityClass);
767: }
768: }
769:
770: if (stripos($mock->getTable(), 'mock') === 0) {
771: $mock->setTable(Inflector::tableize($baseClass));
772: }
773:
774: $locator->set($baseClass, $mock);
775: $locator->set($alias, $mock);
776:
777: return $mock;
778: }
779:
780: /**
781: * Gets the class name for the table.
782: *
783: * @param string $alias The model to get a mock for.
784: * @param array $options The config data for the mock's constructor.
785: * @return string
786: * @throws \Cake\ORM\Exception\MissingTableClassException
787: */
788: protected function _getTableClassName($alias, array $options)
789: {
790: if (empty($options['className'])) {
791: $class = Inflector::camelize($alias);
792: $className = App::className($class, 'Model/Table', 'Table');
793: if (!$className) {
794: throw new MissingTableClassException([$alias]);
795: }
796: $options['className'] = $className;
797: }
798:
799: return $options['className'];
800: }
801:
802: /**
803: * Set the app namespace
804: *
805: * @param string $appNamespace The app namespace, defaults to "TestApp".
806: * @return void
807: */
808: public static function setAppNamespace($appNamespace = 'TestApp')
809: {
810: Configure::write('App.namespace', $appNamespace);
811: }
812: }
813: