Passed
Push — develop ( 268024...404ff9 )
by nguereza
01:50
created

PlatineTestCase::runPrivateProtectedMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 9
rs 10
1
<?php
2
3
/**
4
 * Platine Dev Tools
5
 *
6
 * Platine Dev Tools is a collection of some classes/functions
7
 * designed for development
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Dev Tools
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file PlatineTestCase.php
34
 *
35
 *  The Base class used for test case
36
 *
37
 *  @package    Platine\Dev
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Dev;
49
50
use InvalidArgumentException;
51
use org\bovigo\vfs\vfsStream;
52
use org\bovigo\vfs\vfsStreamContainer;
53
use org\bovigo\vfs\vfsStreamDirectory;
54
use org\bovigo\vfs\vfsStreamFile;
55
use PHPUnit\Framework\TestCase;
56
use ReflectionClass;
57
use ReflectionProperty;
58
59
/**
60
 * @class PlatineTestCase
61
 * @package Platine\Dev
62
 */
63
class PlatineTestCase extends TestCase
64
{
65
    /**
66
     * Method to test private & protected method
67
     *
68
     * @param object $object the class instance to use
69
     * @param string $method the name of the method
70
     * @param array<int, mixed> $args the list of method arguments
71
     * @return mixed
72
     */
73
    public function runPrivateProtectedMethod(
74
        object $object,
75
        string $method,
76
        array $args = []
77
    ) {
78
        $reflection = new ReflectionClass(get_class($object));
79
        $reflectionMethod = $reflection->getMethod($method);
80
        $reflectionMethod->setAccessible(true);
81
        return $reflectionMethod->invokeArgs($object, $args);
82
    }
83
84
    /**
85
     * Method to set/get private & protected attribute
86
     *
87
     * @param string $className the name of the class
88
     * @param string $attr the name of the class attribute
89
     */
90
    public function getPrivateProtectedAttribute(
91
        string $className,
92
        string $attr
93
    ): ReflectionProperty {
94
        $rProp = new ReflectionProperty($className, $attr);
95
        $rProp->setAccessible(true);
96
        return $rProp;
97
    }
98
99
    /**
100
     * Create virtual file with the given content
101
     * @param  string $filename
102
     * @param  vfsStreamContainer<vfsStreamContainerIterator> $destination
103
     * @param  string $content
104
     * @return vfsStreamFile
105
     */
106
    public function createVfsFile(
107
        string $filename,
108
        vfsStreamContainer $destination,
109
        string $content = ''
110
    ): vfsStreamFile {
111
        return vfsStream::newFile($filename)
112
                        ->at($destination)
113
                        ->setContent($content);
114
    }
115
116
    /**
117
     * Create virtual file without content
118
     * @param  string $filename
119
     * @param  vfsStreamContainer<vfsStreamContainerIterator> $destination
120
     * @return vfsStreamFile
121
     */
122
    public function createVfsFileOnly(
123
        string $filename,
124
        vfsStreamContainer $destination
125
    ): vfsStreamFile {
126
        return vfsStream::newFile($filename)
127
                        ->at($destination);
128
    }
129
130
    /**
131
     * Create virtual directory
132
     * @param  string $name
133
     * @param  vfsStreamContainer<vfsStreamContainerIterator> $destination
134
     * @return vfsStreamDirectory
135
     */
136
    public function createVfsDirectory(
137
        string $name,
138
        vfsStreamContainer $destination = null
139
    ): vfsStreamDirectory {
140
        if ($destination) {
141
            return vfsStream::newDirectory($name)->at($destination);
142
        }
143
        return vfsStream::newDirectory($name);
144
    }
145
146
    /**
147
     * Return the list of methods to mocks in the parameters of PHPUnit::TestCase::getMock()
148
     *
149
     * @param class-string<object>|object $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<object>|object at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<object>|object.
Loading history...
150
     * @param string[] $exclude list of methods to exclude
151
     * @return string[]
152
     */
153
    public function getClassMethodsToMock($class, array $exclude = []): array
154
    {
155
        $methods = [];
156
157
        if (is_string($class) && !class_exists($class)) {
158
            throw new InvalidArgumentException(
159
                sprintf('Can not find class [%s]', $class)
160
            );
161
        }
162
163
        $reflectionClass = new ReflectionClass($class);
164
165
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
166
            if (!in_array($reflectionMethod->name, $exclude)) {
167
                $methods[] = $reflectionMethod->name;
168
            }
169
        }
170
171
        return $methods;
172
    }
173
174
    /**
175
     * Get the instance of the given class
176
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
177
     * @param array<string, mixed> $mockMethods
178
     * @param array<int, string> $excludes
179
     * @return mixed
180
     */
181
    public function getMockInstance(
182
        string $class,
183
        array $mockMethods = [],
184
        array $excludes = []
185
    ) {
186
        $methods = $this->getClassMethodsToMock($class, $excludes);
187
188
        $mock = $this->getMockBuilder($class)
189
                    ->disableOriginalConstructor()
190
                    ->onlyMethods($methods)
191
                    ->getMock();
192
193
        foreach ($mockMethods as $method => $returnValue) {
194
            $mock->expects($this->any())
195
                ->method($method)
196
                ->will($this->returnValue($returnValue));
197
        }
198
199
        return $mock;
200
    }
201
202
    /**
203
     * Get the instance of the given class using return map
204
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
205
     * @param array<string, mixed> $mockMethods
206
     * @param array<int, string> $excludes
207
     * @return mixed
208
     */
209
    public function getMockInstanceMap(
210
        string $class,
211
        array $mockMethods = [],
212
        array $excludes = []
213
    ) {
214
        $methods = $this->getClassMethodsToMock($class, $excludes);
215
216
        $mock = $this->getMockBuilder($class)
217
                    ->disableOriginalConstructor()
218
                    ->onlyMethods($methods)
219
                    ->getMock();
220
221
        foreach ($mockMethods as $method => $returnValues) {
222
            $mock->expects($this->any())
223
                ->method($method)
224
                ->will(
225
                    $this->returnValueMap($returnValues)
226
                );
227
        }
228
229
        return $mock;
230
    }
231
232
    /**
233
     * Return the value of private or protected property
234
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
235
     * @param object $instance
236
     * @param string $name
237
     * @return mixed
238
     */
239
    public function getPropertyValue(string $class, object $instance, string $name)
240
    {
241
        $reflection = $this->getPrivateProtectedAttribute($class, $name);
242
        return $reflection->getValue($instance);
243
    }
244
245
    /**
246
     * Set the value of private or protected property
247
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
248
     * @param object $instance
249
     * @param string $name
250
     * @param mixed $value
251
     * @return void
252
     */
253
    public function setPropertyValue(string $class, object $instance, string $name, $value)
254
    {
255
        $reflection = $this->getPrivateProtectedAttribute($class, $name);
256
        $reflection->setValue($instance, $value);
257
    }
258
259
    /**
260
     * Test assert command expected given output
261
     * @param string $expected
262
     * @param string $output
263
     * @return void
264
     */
265
    public function assertCommandOutput(string $expected, string $output): void
266
    {
267
        $result = str_replace("\n", PHP_EOL, $expected);
268
        $this->assertEquals($result, $output);
269
    }
270
271
    /**
272
     * @codeCoverageIgnore
273
     * @return void
274
     */
275
    protected function tearDown(): void
276
    {
277
        //restore all mock variable global value to "false"
278
        foreach ($GLOBALS as $key => $value) {
279
            if (substr((string) $key, 0, 5) === 'mock_') {
280
                $GLOBALS[$key] = false;
281
            }
282
        }
283
    }
284
}
285