Passed
Branch merging-leagues-tournaments (a75688)
by Benedikt
07:29
created

php$0 ➔ getEntityManagerMockForQueries()   B

Complexity

Conditions 3

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 67
rs 8.72
cc 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A UnitTestCase.php$0 ➔ __construct() 0 3 1
A UnitTestCase.php$0 ➔ setLockMode() 0 3 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * Created by PhpStorm.
5
 * User: benedikt
6
 * Date: 1/6/18
7
 * Time: 7:08 PM
8
 */
9
10
namespace Tfboe\FmLib\TestHelpers;
11
12
use ArrayAccess;
13
use Doctrine\ORM\AbstractQuery;
14
use Doctrine\ORM\EntityManager;
15
use Doctrine\ORM\QueryBuilder;
16
use PHPUnit\Framework\MockObject\MockObject;
17
use PHPUnit\Framework\MockObject\Stub;
18
use PHPUnit\Framework\TestCase;
19
use ReflectionClass;
20
use ReflectionException;
21
use Tfboe\FmLib\Entity\Helpers\BaseEntity;
22
use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity;
23
use function class_exists;
24
25
/**
26
 * Class UnitTestCase
27
 * @package Tfboe\FmLib\TestHelpers
28
 */
29
abstract class UnitTestCase extends TestCase
30
{
31
  use ReflectionMethods;
32
  use OnlyTestLogging;
33
34
//<editor-fold desc="Public Methods">
35
36
  /**
37
   * @param array|ArrayAccess $subset
38
   * @param array|ArrayAccess $array
39
   */
40
  public static function assertArrayIsSubset($subset, $array): void
41
  {
42
    foreach ($subset as $key => $value) {
43
      self::assertArrayHasKey($key, $array);
44
      self::assertEquals($value, $array[$key]);
45
    }
46
  }
47
48
  /**
49
   * @inheritDoc
50
   */
51
  public function tearDown(): void
52
  {
53
    parent::tearDown();
54
  }
55
//</editor-fold desc="Public Methods">
56
57
//<editor-fold desc="Protected Final Methods">
58
59
  /**
60
   * Gets a mock class (with full implementation). The given arguments are used for the arguments for the constructor.
61
   * If too less arguments are given mocks are created for the rest of the constructor arguments.
62
   * @param string $className the class to mock
63
   * @param array $arguments the arguments to use for the constructor
64
   * @param string[] $mockedMethods the methods to mock in the class
65
   * @return MockObject the mocked object
66
   * @throws ReflectionException
67
   */
68
  final protected function getMockWithMockedArguments(string $className, array $arguments = [],
69
                                                      array $mockedMethods = []): MockObject
70
  {
71
72
    $reflection = new ReflectionClass($className);
73
    $params = $reflection->getConstructor()->getParameters();
74
    $allArguments = $arguments;
75
    for ($i = count($arguments), $c = count($params); $i < $c; $i++) {
76
      $allArguments[] = $this->createMock($params[$i]->getClass()->name);
77
    }
78
    return $this->getMockForAbstractClass($className, $allArguments, '', true, true, true, $mockedMethods);
79
  }
80
81
  /**
82
   * Gets a new instance of the given class. The given arguments are used for the arguments for the constructor.
83
   * If too less arguments are given mocks are created for the rest of the constructor arguments.
84
   * @param string $className the class for which to create an instance
85
   * @param array $arguments the arguments to use for the constructor
86
   * @return mixed an instance of the given class
87
   * @throws ReflectionException
88
   */
89
  final protected function getObjectWithMockedArguments($className, array $arguments = [])
90
  {
91
92
    $reflection = new ReflectionClass($className);
93
    $params = $reflection->getConstructor()->getParameters();
94
    $allArguments = $arguments;
95
    for ($i = count($arguments), $c = count($params); $i < $c; $i++) {
96
      $allArguments[$i] = $this->createMock($params[$i]->getClass()->name);
97
    }
98
    return new $className(...$allArguments);
99
  }
100
//</editor-fold desc="Protected Final Methods">
101
102
//<editor-fold desc="Protected Methods">
103
104
  /**
105
   * @param string|string[] $originalClassName
106
   * @param array $methods
107
   * @return MockObject
108
   */
109
  protected function createPartialMock($originalClassName, array $methods): MockObject
110
  {
111
    return parent::createPartialMock($originalClassName, $methods); // TODO: Change the autogenerated stub
112
  }
113
114
  /**
115
   * Creates a stub with a given set of stubbed methods, which will return the given results
116
   * @param string $class the class name
117
   * @param array $methodResults a dictionary mapping method names to results of this methods
118
   * @return MockObject|mixed the configured stub
119
   */
120
  protected function getStub(string $class, array $methodResults = []): Stub
121
  {
122
    return $this->createConfiguredMock($class, $methodResults);
123
  }
124
125
  /**
126
   * Creates an empty mock with a getId method
127
   * @param string $class the class to mock
128
   * @param string $entityId the id to assign
129
   * @param string $getterMethod the name of the getter method
130
   * @return MockObject|mixed the mocked instance
131
   */
132
  protected function createStubWithId(string $class, $entityId = "entity-id", $getterMethod = 'getId')
133
  {
134
    return $this->getStub($class, [$getterMethod => $entityId]);
135
  }
136
137
  /**
138
   * Gets a mock for an entity manager which creates a query builder which will return a query which will return the
139
   * given result.
140
   * @param array $results the result arrays the queries should return
141
   * @param string[] $expectedQueries the expected queries if set
142
   * @param string[] $otherMockedMethods list of other methods to mock
143
   * @param bool $withSetLockMode
144
   * @return MockObject|EntityManager the mocked entity manager
145
   */
146
  protected function getEntityManagerMockForQueries(array $results, array $expectedQueries = [],
147
                                                    array $otherMockedMethods = [],
148
                                                    bool $withSetLockMode = false): MockObject
149
  {
150
    $entityManager = $this->getMockForAbstractClass(EntityManager::class, [], '',
151
      false, true, true, array_merge($otherMockedMethods, ['createQueryBuilder']));
152
    assert($expectedQueries == [] || count($results) === count($expectedQueries));
153
    $entityManager->expects(static::exactly(count($results)))->method('createQueryBuilder')->willReturnCallback(
154
      function () use ($entityManager, &$results, &$expectedQueries, $withSetLockMode) {
155
        $queryBuilder = $this->getMockForAbstractClass(QueryBuilder::class, [$entityManager],
156
          '', true, true, true, ['getQuery']);
157
        /** @var MockObject|AbstractQuery $query */
158
        $query = $this->getMockBuilder(AbstractQuery::class)
159
          ->disableOriginalConstructor()
160
          ->disableOriginalClone()
161
          ->disableArgumentCloning()
162
          ->disallowMockingUnknownTypes()
163
          ->onlyMethods(['getSQL', '_doExecute', 'getResult', 'getOneOrNullResult'])
164
          ->getMock();
165
        $query->expects(static::once())->method('getResult')->willReturn(array_shift($results));
166
        $query->expects(static::atMost(1))->method('getOneOrNullResult')->willReturnCallback(
167
          function () use ($query) {
168
            $res = $query->getResult();
169
            if (count($res) === 0) {
170
              return null;
171
            } else {
172
              reset($res);
173
              return $res[key($res)];
174
            }
175
          }
176
        );
177
        if ($withSetLockMode) {
178
          $query = new class($query) {
179
            private $query;
180
181
            /**
182
             *  constructor.
183
             * @param AbstractQuery $query
184
             */
185
            public function __construct(AbstractQuery $query)
186
            {
187
              $this->query = $query;
188
            }
189
190
            /**
191
             * @return AbstractQuery
192
             */
193
            public function setLockMode()
194
            {
195
              return $this->query;
196
            }
197
          };
198
        }
199
        if ($expectedQueries !== []) {
200
          $queryBuilder->expects(static::once())->method('getQuery')->willReturnCallback(
201
            function () use ($queryBuilder, $query, &$expectedQueries) {
202
              /** @var QueryBuilder $queryBuilder */
203
              self::assertEquals(array_shift($expectedQueries), $queryBuilder->getDQL());
204
              return $query;
205
            });
206
        } else {
207
          $queryBuilder->expects(static::once())->method('getQuery')->willReturn($query);
208
        }
209
        return $queryBuilder;
210
      }
211
    );
212
    return $entityManager;
213
  }
214
215
  /**
216
   * Gets a mock for an entity manager which creates a query builder which will return a query which will return the
217
   * given result.
218
   * @param array $result the result array the query should return
219
   * @param string|null $expectedQuery the expected query if set
220
   * @param string[] $otherMockedMethods list of other methods to mock
221
   * @param int $amount the number of times this query gets sent
222
   * @param bool $withSetLockMode
223
   * @return MockObject|EntityManager the mocked entity manager
224
   */
225
  protected function getEntityManagerMockForQuery(array $result, ?string $expectedQuery = null,
226
                                                  array $otherMockedMethods = [], $amount = 1,
227
                                                  bool $withSetLockMode = false): MockObject
228
  {
229
    return $this->getEntityManagerMockForQueries(array_fill(0, $amount, $result),
230
      $expectedQuery === null ? [] : array_fill(0, $amount, $expectedQuery), $otherMockedMethods, $withSetLockMode);
231
  }
232
233
  /**
234
   * @param string $className
235
   * @param array $methodNames
236
   * @param array $additionalInterfaces
237
   * @param string|null $baseClass
238
   * @param bool $callParentConstructor
239
   * @param bool $hasInit
240
   * @return MockObject
241
   * @noinspection PhpTooManyParametersInspection
242
   */
243
  protected function getMockedEntity(string $className, array $methodNames = [], array $additionalInterfaces = [],
244
                                     ?string $baseClass = BaseEntity::class,
245
                                     bool $callParentConstructor = false,
246
                                     bool $hasInit = true): MockObject
247
  {
248
    $dynNamespace = "Dynamic\\Generated";
249
    $dynClassName = $dynNamespace . "\\" . $className;
250
    if (!class_exists($dynClassName, false)) {
251
      $base = $baseClass === null ? "" : "extends \\$baseClass ";
252
      $namespace = "\\Tfboe\\FmLib\\Entity\\";
253
      $additionalInterfaces[] = $namespace . $className . "Interface";
254
      $interfaces = implode(", \\", $additionalInterfaces);
255
      $parentConstructor = $callParentConstructor ? "parent::__construct();" : "";
256
      $init = $hasInit ? "\$this->init();" : "";
257
      $class = <<<CLASS
258
namespace $dynNamespace;
259
class $className ${base}implements $interfaces
260
{
261
  use ${namespace}Traits\\${className};
262
  public function __construct()
263
  {
264
    $parentConstructor
265
    $init
266
  }
267
}
268
CLASS;
269
      eval($class);
270
    }
271
    return $this->getMockForAbstractClass($dynClassName, [], '', true, true, true, $methodNames);
272
  }
273
274
  /**
275
   * @param string $className
276
   * @param array $methods
277
   * @param array $additionalInterfaces
278
   * @return MockObject
279
   */
280
  protected function getStubbedTournamentHierarchyEntity(string $className, array $methods = [],
281
                                                         array $additionalInterfaces = [])
282
  {
283
    return $this->getStubbedEntity($className, $methods, $additionalInterfaces, TournamentHierarchyEntity::class, true);
284
  }
285
286
  /**
287
   * @param $traitName
288
   * @param array $methods
289
   * @param array $arguments
290
   * @param string $mockClassName
291
   * @param bool $callOriginalConstructor
292
   * @param bool $callOriginalClone
293
   * @param bool $callAutoload
294
   * @param bool $cloneArguments
295
   * @return MockObject
296
   * @noinspection PhpTooManyParametersInspection
297
   */
298
  protected function getPartialMockForTrait($traitName, array $methods, array $arguments = [],
299
                                            $mockClassName = '', $callOriginalConstructor = true,
300
                                            $callOriginalClone = true, $callAutoload = true,
301
                                            $cloneArguments = false): MockObject
302
  {
303
    $o = $this->getMockForTrait($traitName, $arguments, $mockClassName, $callOriginalConstructor,
304
      $callOriginalClone, $callAutoload, array_keys($methods), $cloneArguments);
305
    $this->stubMethods($o, $methods);
306
    return $o;
307
  }
308
309
  /**
310
   * @param string $className
311
   * @param array $methods
312
   * @param array $additionalInterfaces
313
   * @param string|null $baseClass
314
   * @param bool $callParentConstructor
315
   * @param bool $hasInit
316
   * @return MockObject
317
   * @noinspection PhpTooManyParametersInspection
318
   */
319
  protected function getStubbedEntity(string $className, array $methods = [], array $additionalInterfaces = [],
320
                                      ?string $baseClass = BaseEntity::class,
321
                                      bool $callParentConstructor = false,
322
                                      bool $hasInit = true): MockObject
323
  {
324
    $o = $this->getMockedEntity($className, array_keys($methods), $additionalInterfaces, $baseClass,
325
      $callParentConstructor, $hasInit);
326
    $this->stubMethods($o, $methods);
327
    return $o;
328
  }
329
330
  /**
331
   * @param MockObject $o
332
   * @param $methods
333
   */
334
  protected function stubMethods(MockObject $o, $methods)
335
  {
336
    foreach ($methods as $method => $return) {
337
      $o->method($method)->willReturn($return);
338
    }
339
  }
340
//</editor-fold desc="Protected Methods">
341
}