TestCase::getMockForModel()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 3
dl 0
loc 37
rs 9.0168
c 0
b 0
f 0
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
    use LocatorAwareTrait;
38
39
    /**
40
     * The class responsible for managing the creation, loading and removing of fixtures
41
     *
42
     * @var \Cake\TestSuite\Fixture\FixtureManager|null
43
     */
44
    public $fixtureManager;
45
46
    /**
47
     * By default, all fixtures attached to this class will be truncated and reloaded after each test.
48
     * Set this to false to handle manually
49
     *
50
     * @var bool
51
     */
52
    public $autoFixtures = true;
53
54
    /**
55
     * Control table create/drops on each test method.
56
     *
57
     * If true, tables will still be dropped at the
58
     * end of each test runner execution.
59
     *
60
     * @var bool
61
     */
62
    public $dropTables = false;
63
64
    /**
65
     * Configure values to restore at end of test.
66
     *
67
     * @var array
68
     */
69
    protected $_configure = [];
70
71
    /**
72
     * Path settings to restore at the end of the test.
73
     *
74
     * @var array
75
     */
76
    protected $_pathRestore = [];
77
78
    /**
79
     * Overrides SimpleTestCase::skipIf to provide a boolean return value
80
     *
81
     * @param bool $shouldSkip Whether or not the test should be skipped.
82
     * @param string $message The message to display.
83
     * @return bool
84
     */
85
    public function skipIf($shouldSkip, $message = '')
86
    {
87
        if ($shouldSkip) {
88
            $this->markTestSkipped($message);
89
        }
90
91
        return $shouldSkip;
92
    }
93
94
    /**
95
     * Helper method for tests that needs to use error_reporting()
96
     *
97
     * @param int $errorLevel value of error_reporting() that needs to use
98
     * @param callable $callable callable function that will receive asserts
99
     * @return void
100
     */
101
    public function withErrorReporting($errorLevel, $callable)
102
    {
103
        $default = error_reporting();
104
        error_reporting($errorLevel);
105
        try {
106
            $callable();
107
        } finally {
108
            error_reporting($default);
109
        }
110
    }
111
112
    /**
113
     * Helper method for check deprecation methods
114
     *
115
     * @param callable $callable callable function that will receive asserts
116
     * @return void
117
     */
118
    public function deprecated($callable)
119
    {
120
        $errorLevel = error_reporting();
121
        error_reporting(E_ALL ^ E_USER_DEPRECATED);
122
        try {
123
            $callable();
124
        } finally {
125
            error_reporting($errorLevel);
126
        }
127
    }
128
129
    /**
130
     * Setup the test case, backup the static object values so they can be restored.
131
     * Specifically backs up the contents of Configure and paths in App if they have
132
     * not already been backed up.
133
     *
134
     * @return void
135
     */
136
    public function setUp()
137
    {
138
        parent::setUp();
139
140
        if (!$this->_configure) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_configure of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
141
            $this->_configure = Configure::read();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Cake\Core\Configure::read() of type * is incompatible with the declared type array of property $_configure.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
142
        }
143
        if (class_exists('Cake\Routing\Router', false)) {
144
            Router::reload();
145
        }
146
147
        EventManager::instance(new EventManager());
148
    }
149
150
    /**
151
     * teardown any static object changes and restore them.
152
     *
153
     * @return void
154
     */
155
    public function tearDown()
156
    {
157
        parent::tearDown();
158
        if ($this->_configure) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_configure of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
159
            Configure::clear();
160
            Configure::write($this->_configure);
161
        }
162
        $this->getTableLocator()->clear();
163
    }
164
165
    /**
166
     * Chooses which fixtures to load for a given test
167
     *
168
     * Each parameter is a model name that corresponds to a fixture, i.e. 'Posts', 'Authors', etc.
169
     * Passing no parameters will cause all fixtures on the test case to load.
170
     *
171
     * @return void
172
     * @see \Cake\TestSuite\TestCase::$autoFixtures
173
     * @throws \Exception when no fixture manager is available.
174
     */
175
    public function loadFixtures()
176
    {
177
        if ($this->fixtureManager === null) {
178
            throw new Exception('No fixture manager to load the test fixture');
179
        }
180
        $args = func_get_args();
181
        foreach ($args as $class) {
182
            $this->fixtureManager->loadSingle($class, null, $this->dropTables);
183
        }
184
185
        if (empty($args)) {
186
            $autoFixtures = $this->autoFixtures;
187
            $this->autoFixtures = true;
188
            $this->fixtureManager->load($this);
189
            $this->autoFixtures = $autoFixtures;
190
        }
191
    }
192
193
    /**
194
     * Load plugins into a simulated application.
195
     *
196
     * Useful to test how plugins being loaded/not loaded interact with other
197
     * elements in CakePHP or applications.
198
     *
199
     * @param array $plugins List of Plugins to load.
200
     * @return \Cake\Http\BaseApplication
201
     */
202
    public function loadPlugins(array $plugins = [])
203
    {
204
        /** @var \Cake\Http\BaseApplication $app */
205
        $app = $this->getMockForAbstractClass(
206
            BaseApplication::class,
207
            ['']
208
        );
209
210
        foreach ($plugins as $pluginName => $config) {
211
            if (is_array($config)) {
212
                $app->addPlugin($pluginName, $config);
213
            } else {
214
                $app->addPlugin($config);
215
            }
216
        }
217
        $app->pluginBootstrap();
218
        $builder = Router::createRouteBuilder('/');
219
        $app->pluginRoutes($builder);
220
221
        return $app;
222
    }
223
224
    /**
225
     * Remove plugins from the global plugin collection.
226
     *
227
     * Useful in test case teardown methods.
228
     *
229
     * @param string[] $names A list of plugins you want to remove.
230
     * @return void
231
     */
232
    public function removePlugins(array $names = [])
233
    {
234
        $collection = Plugin::getCollection();
235
        foreach ($names as $name) {
236
            $collection->remove($name);
237
        }
238
    }
239
240
    /**
241
     * Clear all plugins from the global plugin collection.
242
     *
243
     * Useful in test case teardown methods.
244
     *
245
     * @return void
246
     */
247
    public function clearPlugins()
248
    {
249
        Plugin::getCollection()->clear();
250
    }
251
252
    /**
253
     * Asserts that a global event was fired. You must track events in your event manager for this assertion to work
254
     *
255
     * @param string $name Event name
256
     * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
257
     * @param string $message Assertion failure message
258
     * @return void
259
     */
260
    public function assertEventFired($name, $eventManager = null, $message = '')
261
    {
262
        if (!$eventManager) {
263
            $eventManager = EventManager::instance();
264
        }
265
        $this->assertThat($name, new EventFired($eventManager), $message);
266
    }
267
268
    /**
269
     * Asserts an event was fired with data
270
     *
271
     * If a third argument is passed, that value is used to compare with the value in $dataKey
272
     *
273
     * @param string $name Event name
274
     * @param string $dataKey Data key
275
     * @param string $dataValue Data value
276
     * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
277
     * @param string $message Assertion failure message
278
     * @return void
279
     */
280
    public function assertEventFiredWith($name, $dataKey, $dataValue, $eventManager = null, $message = '')
281
    {
282
        if (!$eventManager) {
283
            $eventManager = EventManager::instance();
284
        }
285
        $this->assertThat($name, new EventFiredWith($eventManager, $dataKey, $dataValue), $message);
286
    }
287
288
    /**
289
     * Assert text equality, ignoring differences in newlines.
290
     * Helpful for doing cross platform tests of blocks of text.
291
     *
292
     * @param string $expected The expected value.
293
     * @param string $result The actual value.
294
     * @param string $message The message to use for failure.
295
     * @return void
296
     */
297
    public function assertTextNotEquals($expected, $result, $message = '')
298
    {
299
        $expected = str_replace(["\r\n", "\r"], "\n", $expected);
300
        $result = str_replace(["\r\n", "\r"], "\n", $result);
301
        $this->assertNotEquals($expected, $result, $message);
302
    }
303
304
    /**
305
     * Assert text equality, ignoring differences in newlines.
306
     * Helpful for doing cross platform tests of blocks of text.
307
     *
308
     * @param string $expected The expected value.
309
     * @param string $result The actual value.
310
     * @param string $message The message to use for failure.
311
     * @return void
312
     */
313
    public function assertTextEquals($expected, $result, $message = '')
314
    {
315
        $expected = str_replace(["\r\n", "\r"], "\n", $expected);
316
        $result = str_replace(["\r\n", "\r"], "\n", $result);
317
        $this->assertEquals($expected, $result, $message);
318
    }
319
320
    /**
321
     * Asserts that a string starts with a given prefix, ignoring differences in newlines.
322
     * Helpful for doing cross platform tests of blocks of text.
323
     *
324
     * @param string $prefix The prefix to check for.
325
     * @param string $string The string to search in.
326
     * @param string $message The message to use for failure.
327
     * @return void
328
     */
329
    public function assertTextStartsWith($prefix, $string, $message = '')
330
    {
331
        $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
332
        $string = str_replace(["\r\n", "\r"], "\n", $string);
333
        $this->assertStringStartsWith($prefix, $string, $message);
334
    }
335
336
    /**
337
     * Asserts that a string starts not with a given prefix, ignoring differences in newlines.
338
     * Helpful for doing cross platform tests of blocks of text.
339
     *
340
     * @param string $prefix The prefix to not find.
341
     * @param string $string The string to search.
342
     * @param string $message The message to use for failure.
343
     * @return void
344
     */
345
    public function assertTextStartsNotWith($prefix, $string, $message = '')
346
    {
347
        $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
348
        $string = str_replace(["\r\n", "\r"], "\n", $string);
349
        $this->assertStringStartsNotWith($prefix, $string, $message);
350
    }
351
352
    /**
353
     * Asserts that a string ends with a given prefix, ignoring differences in newlines.
354
     * Helpful for doing cross platform tests of blocks of text.
355
     *
356
     * @param string $suffix The suffix to find.
357
     * @param string $string The string to search.
358
     * @param string $message The message to use for failure.
359
     * @return void
360
     */
361
    public function assertTextEndsWith($suffix, $string, $message = '')
362
    {
363
        $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
364
        $string = str_replace(["\r\n", "\r"], "\n", $string);
365
        $this->assertStringEndsWith($suffix, $string, $message);
366
    }
367
368
    /**
369
     * Asserts that a string ends not with a given prefix, ignoring differences in newlines.
370
     * Helpful for doing cross platform tests of blocks of text.
371
     *
372
     * @param string $suffix The suffix to not find.
373
     * @param string $string The string to search.
374
     * @param string $message The message to use for failure.
375
     * @return void
376
     */
377
    public function assertTextEndsNotWith($suffix, $string, $message = '')
378
    {
379
        $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
380
        $string = str_replace(["\r\n", "\r"], "\n", $string);
381
        $this->assertStringEndsNotWith($suffix, $string, $message);
382
    }
383
384
    /**
385
     * Assert that a string contains another string, ignoring differences in newlines.
386
     * Helpful for doing cross platform tests of blocks of text.
387
     *
388
     * @param string $needle The string to search for.
389
     * @param string $haystack The string to search through.
390
     * @param string $message The message to display on failure.
391
     * @param bool $ignoreCase Whether or not the search should be case-sensitive.
392
     * @return void
393
     */
394
    public function assertTextContains($needle, $haystack, $message = '', $ignoreCase = false)
395
    {
396
        $needle = str_replace(["\r\n", "\r"], "\n", $needle);
397
        $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
398
        $this->assertContains($needle, $haystack, $message, $ignoreCase);
399
    }
400
401
    /**
402
     * Assert that a text doesn't contain another text, ignoring differences in newlines.
403
     * Helpful for doing cross platform tests of blocks of text.
404
     *
405
     * @param string $needle The string to search for.
406
     * @param string $haystack The string to search through.
407
     * @param string $message The message to display on failure.
408
     * @param bool $ignoreCase Whether or not the search should be case-sensitive.
409
     * @return void
410
     */
411
    public function assertTextNotContains($needle, $haystack, $message = '', $ignoreCase = false)
412
    {
413
        $needle = str_replace(["\r\n", "\r"], "\n", $needle);
414
        $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
415
        $this->assertNotContains($needle, $haystack, $message, $ignoreCase);
416
    }
417
418
    /**
419
     * Asserts HTML tags.
420
     *
421
     * @param string $string An HTML/XHTML/XML string
422
     * @param array $expected An array, see above
423
     * @param bool $fullDebug Whether or not more verbose output should be used.
424
     * @return void
425
     * @deprecated 3.0. Use assertHtml() instead.
426
     */
427
    public function assertTags($string, $expected, $fullDebug = false)
428
    {
429
        deprecationWarning('TestCase::assertTags() is deprecated. Use TestCase::assertHtml() instead.');
430
        $this->assertHtml($expected, $string, $fullDebug);
431
    }
432
433
    /**
434
     * Asserts HTML tags.
435
     *
436
     * Takes an array $expected and generates a regex from it to match the provided $string.
437
     * Samples for $expected:
438
     *
439
     * Checks for an input tag with a name attribute (contains any non-empty value) and an id
440
     * attribute that contains 'my-input':
441
     *
442
     * ```
443
     * ['input' => ['name', 'id' => 'my-input']]
444
     * ```
445
     *
446
     * Checks for two p elements with some text in them:
447
     *
448
     * ```
449
     * [
450
     *   ['p' => true],
451
     *   'textA',
452
     *   '/p',
453
     *   ['p' => true],
454
     *   'textB',
455
     *   '/p'
456
     * ]
457
     * ```
458
     *
459
     * You can also specify a pattern expression as part of the attribute values, or the tag
460
     * being defined, if you prepend the value with preg: and enclose it with slashes, like so:
461
     *
462
     * ```
463
     * [
464
     *   ['input' => ['name', 'id' => 'preg:/FieldName\d+/']],
465
     *   'preg:/My\s+field/'
466
     * ]
467
     * ```
468
     *
469
     * Important: This function is very forgiving about whitespace and also accepts any
470
     * permutation of attribute order. It will also allow whitespace between specified tags.
471
     *
472
     * @param array $expected An array, see above
473
     * @param string $string An HTML/XHTML/XML string
474
     * @param bool $fullDebug Whether or not more verbose output should be used.
475
     * @return bool
476
     */
477
    public function assertHtml($expected, $string, $fullDebug = false)
478
    {
479
        $regex = [];
480
        $normalized = [];
481
        foreach ((array)$expected as $key => $val) {
482
            if (!is_numeric($key)) {
483
                $normalized[] = [$key => $val];
484
            } else {
485
                $normalized[] = $val;
486
            }
487
        }
488
        $i = 0;
489
        foreach ($normalized as $tags) {
490
            if (!is_array($tags)) {
491
                $tags = (string)$tags;
492
            }
493
            $i++;
494
            if (is_string($tags) && $tags[0] === '<') {
495
                $tags = [substr($tags, 1) => []];
496
            } elseif (is_string($tags)) {
497
                $tagsTrimmed = preg_replace('/\s+/m', '', $tags);
498
499
                if (preg_match('/^\*?\//', $tags, $match) && $tagsTrimmed !== '//') {
500
                    $prefix = [null, null];
501
502
                    if ($match[0] === '*/') {
503
                        $prefix = ['Anything, ', '.*?'];
504
                    }
505
                    $regex[] = [
506
                        sprintf('%sClose %s tag', $prefix[0], substr($tags, strlen($match[0]))),
507
                        sprintf('%s\s*<[\s]*\/[\s]*%s[\s]*>[\n\r]*', $prefix[1], substr($tags, strlen($match[0]))),
508
                        $i,
509
                    ];
510
                    continue;
511
                }
512
                if (!empty($tags) && preg_match('/^preg\:\/(.+)\/$/i', $tags, $matches)) {
513
                    $tags = $matches[1];
514
                    $type = 'Regex matches';
515
                } else {
516
                    $tags = '\s*' . preg_quote($tags, '/');
517
                    $type = 'Text equals';
518
                }
519
                $regex[] = [
520
                    sprintf('%s "%s"', $type, $tags),
521
                    $tags,
522
                    $i,
523
                ];
524
                continue;
525
            }
526
            foreach ($tags as $tag => $attributes) {
527
                $regex[] = [
528
                    sprintf('Open %s tag', $tag),
529
                    sprintf('[\s]*<%s', preg_quote($tag, '/')),
530
                    $i,
531
                ];
532
                if ($attributes === true) {
533
                    $attributes = [];
534
                }
535
                $attrs = [];
536
                $explanations = [];
537
                $i = 1;
538
                foreach ($attributes as $attr => $val) {
539
                    if (is_numeric($attr) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
540
                        $attrs[] = $matches[1];
541
                        $explanations[] = sprintf('Regex "%s" matches', $matches[1]);
542
                        continue;
543
                    }
544
545
                    $quotes = '["\']';
546
                    if (is_numeric($attr)) {
547
                        $attr = $val;
548
                        $val = '.+?';
549
                        $explanations[] = sprintf('Attribute "%s" present', $attr);
550
                    } elseif (!empty($val) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
551
                        $val = str_replace(
552
                            ['.*', '.+'],
553
                            ['.*?', '.+?'],
554
                            $matches[1]
555
                        );
556
                        $quotes = $val !== $matches[1] ? '["\']' : '["\']?';
557
558
                        $explanations[] = sprintf('Attribute "%s" matches "%s"', $attr, $val);
559
                    } else {
560
                        $explanations[] = sprintf('Attribute "%s" == "%s"', $attr, $val);
561
                        $val = preg_quote($val, '/');
562
                    }
563
                    $attrs[] = '[\s]+' . preg_quote($attr, '/') . '=' . $quotes . $val . $quotes;
564
                    $i++;
565
                }
566
                if ($attrs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attrs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
567
                    $regex[] = [
568
                        'explains' => $explanations,
569
                        'attrs' => $attrs,
570
                    ];
571
                }
572
                $regex[] = [
573
                    sprintf('End %s tag', $tag),
574
                    '[\s]*\/?[\s]*>[\n\r]*',
575
                    $i,
576
                ];
577
            }
578
        }
579
        foreach ($regex as $i => $assertion) {
580
            $matches = false;
581
            if (isset($assertion['attrs'])) {
582
                $string = $this->_assertAttributes($assertion, $string, $fullDebug, $regex);
583
                if ($fullDebug === true && $string === false) {
584
                    debug($string, true);
585
                    debug($regex, true);
586
                }
587
                continue;
588
            }
589
590
            list($description, $expressions, $itemNum) = $assertion;
591
            $expression = null;
592
            foreach ((array)$expressions as $expression) {
593
                $expression = sprintf('/^%s/s', $expression);
594
                if (preg_match($expression, $string, $match)) {
595
                    $matches = true;
596
                    $string = substr($string, strlen($match[0]));
597
                    break;
598
                }
599
            }
600
            if (!$matches) {
601
                if ($fullDebug === true) {
602
                    debug($string);
603
                    debug($regex);
604
                }
605
                $this->assertRegExp($expression, $string, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
606
607
                return false;
608
            }
609
        }
610
611
        $this->assertTrue(true, '%s');
612
613
        return true;
614
    }
615
616
    /**
617
     * Check the attributes as part of an assertTags() check.
618
     *
619
     * @param array $assertions Assertions to run.
620
     * @param string $string The HTML string to check.
621
     * @param bool $fullDebug Whether or not more verbose output should be used.
622
     * @param array|string $regex Full regexp from `assertHtml`
623
     * @return string|bool
624
     */
625
    protected function _assertAttributes($assertions, $string, $fullDebug = false, $regex = '')
626
    {
627
        $asserts = $assertions['attrs'];
628
        $explains = $assertions['explains'];
629
        do {
630
            $matches = false;
631
            $j = null;
632
            foreach ($asserts as $j => $assert) {
633
                if (preg_match(sprintf('/^%s/s', $assert), $string, $match)) {
634
                    $matches = true;
635
                    $string = substr($string, strlen($match[0]));
636
                    array_splice($asserts, $j, 1);
637
                    array_splice($explains, $j, 1);
638
                    break;
639
                }
640
            }
641
            if ($matches === false) {
642
                if ($fullDebug === true) {
643
                    debug($string);
644
                    debug($regex);
645
                }
646
                $this->assertTrue(false, 'Attribute did not match. Was expecting ' . $explains[$j]);
647
            }
648
            $len = count($asserts);
649
        } while ($len > 0);
650
651
        return $string;
652
    }
653
654
    /**
655
     * Normalize a path for comparison.
656
     *
657
     * @param string $path Path separated by "/" slash.
658
     * @return string Normalized path separated by DIRECTORY_SEPARATOR.
659
     */
660
    protected function _normalizePath($path)
661
    {
662
        return str_replace('/', DIRECTORY_SEPARATOR, $path);
663
    }
664
665
// @codingStandardsIgnoreStart
666
667
    /**
668
     * Compatibility function to test if a value is between an acceptable range.
669
     *
670
     * @param float $expected
671
     * @param float $result
672
     * @param float $margin the rage of acceptation
673
     * @param string $message the text to display if the assertion is not correct
674
     * @return void
675
     */
676
    protected static function assertWithinRange($expected, $result, $margin, $message = '')
677
    {
678
        $upper = $result + $margin;
679
        $lower = $result - $margin;
680
        static::assertTrue(($expected <= $upper) && ($expected >= $lower), $message);
681
    }
682
683
    /**
684
     * Compatibility function to test if a value is not between an acceptable range.
685
     *
686
     * @param float $expected
687
     * @param float $result
688
     * @param float $margin the rage of acceptation
689
     * @param string $message the text to display if the assertion is not correct
690
     * @return void
691
     */
692
    protected static function assertNotWithinRange($expected, $result, $margin, $message = '')
693
    {
694
        $upper = $result + $margin;
695
        $lower = $result - $margin;
696
        static::assertTrue(($expected > $upper) || ($expected < $lower), $message);
697
    }
698
699
    /**
700
     * Compatibility function to test paths.
701
     *
702
     * @param string $expected
703
     * @param string $result
704
     * @param string $message the text to display if the assertion is not correct
705
     * @return void
706
     */
707
    protected static function assertPathEquals($expected, $result, $message = '')
708
    {
709
        $expected = str_replace(DIRECTORY_SEPARATOR, '/', $expected);
710
        $result = str_replace(DIRECTORY_SEPARATOR, '/', $result);
711
        static::assertEquals($expected, $result, $message);
712
    }
713
714
    /**
715
     * Compatibility function for skipping.
716
     *
717
     * @param bool $condition Condition to trigger skipping
718
     * @param string $message Message for skip
719
     * @return bool
720
     */
721
    protected function skipUnless($condition, $message = '')
722
    {
723
        if (!$condition) {
724
            $this->markTestSkipped($message);
725
        }
726
727
        return $condition;
728
    }
729
730
// @codingStandardsIgnoreEnd
731
732
    /**
733
     * Mock a model, maintain fixtures and table association
734
     *
735
     * @param string $alias The model to get a mock for.
736
     * @param string[]|null $methods The list of methods to mock
737
     * @param array $options The config data for the mock's constructor.
738
     * @throws \Cake\ORM\Exception\MissingTableClassException
739
     * @return \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject
740
     */
741
    public function getMockForModel($alias, $methods = [], array $options = [])
742
    {
743
        /** @var \Cake\ORM\Table $className */
744
        $className = $this->_getTableClassName($alias, $options);
745
        $connectionName = $className::defaultConnectionName();
746
        $connection = ConnectionManager::get($connectionName);
747
748
        $locator = $this->getTableLocator();
749
750
        list(, $baseClass) = pluginSplit($alias);
751
        $options += ['alias' => $baseClass, 'connection' => $connection];
752
        $options += $locator->getConfig($alias);
0 ignored issues
show
Bug introduced by
The method getConfig() does not exist on Cake\ORM\Locator\LocatorInterface. Did you maybe mean config()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
753
754
        /** @var \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject $mock */
755
        $mock = $this->getMockBuilder($className)
0 ignored issues
show
Documentation introduced by
$className is of type object<Cake\ORM\Table>, but the function expects a string|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
756
            ->setMethods($methods)
757
            ->setConstructorArgs([$options])
758
            ->getMock();
759
760
        if (empty($options['entityClass']) && $mock->getEntityClass() === Entity::class) {
0 ignored issues
show
Bug introduced by
The method getEntityClass does only exist in Cake\ORM\Table, but not in PHPUnit_Framework_MockObject_MockObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
761
            $parts = explode('\\', $className);
762
            $entityAlias = Inflector::classify(Inflector::underscore(substr(array_pop($parts), 0, -5)));
763
            $entityClass = implode('\\', array_slice($parts, 0, -1)) . '\\Entity\\' . $entityAlias;
764
            if (class_exists($entityClass)) {
765
                $mock->setEntityClass($entityClass);
0 ignored issues
show
Bug introduced by
The method setEntityClass does only exist in Cake\ORM\Table, but not in PHPUnit_Framework_MockObject_MockObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
766
            }
767
        }
768
769
        if (stripos($mock->getTable(), 'mock') === 0) {
0 ignored issues
show
Bug introduced by
The method getTable does only exist in Cake\ORM\Table, but not in PHPUnit_Framework_MockObject_MockObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
770
            $mock->setTable(Inflector::tableize($baseClass));
0 ignored issues
show
Bug introduced by
The method setTable does only exist in Cake\ORM\Table, but not in PHPUnit_Framework_MockObject_MockObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
771
        }
772
773
        $locator->set($baseClass, $mock);
774
        $locator->set($alias, $mock);
775
776
        return $mock;
777
    }
778
779
    /**
780
     * Gets the class name for the table.
781
     *
782
     * @param string $alias The model to get a mock for.
783
     * @param array $options The config data for the mock's constructor.
784
     * @return string
785
     * @throws \Cake\ORM\Exception\MissingTableClassException
786
     */
787
    protected function _getTableClassName($alias, array $options)
788
    {
789
        if (empty($options['className'])) {
790
            $class = Inflector::camelize($alias);
791
            $className = App::className($class, 'Model/Table', 'Table');
792
            if (!$className) {
793
                throw new MissingTableClassException([$alias]);
794
            }
795
            $options['className'] = $className;
796
        }
797
798
        return $options['className'];
799
    }
800
801
    /**
802
     * Set the app namespace
803
     *
804
     * @param string $appNamespace The app namespace, defaults to "TestApp".
805
     * @return void
806
     */
807
    public static function setAppNamespace($appNamespace = 'TestApp')
808
    {
809
        Configure::write('App.namespace', $appNamespace);
810
    }
811
}
812