Completed
Push — master ( 06230d...1ef137 )
by Thomas
02:30
created

MockTrait::ormAllowUpdate()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 28
ccs 0
cts 0
cp 0
rs 9.7666
c 0
b 0
f 0
cc 3
nc 1
nop 3
crap 12
1
<?php /** @noinspection PhpDocMissingThrowsInspection */
2
3
namespace ORM;
4
5
use Mockery as m;
6
use ORM\Exception\IncompletePrimaryKey;
7
8
/**
9
 * MockTrait for ORM
10
 *
11
 * @package ORM
12
 * @author  Thomas Flori <[email protected]>
13
 */
14
trait MockTrait
15
{
16
    /**
17
     * Initialize an EntityManager mock object
18
     *
19
     * The mock is partial and you can map and act with it as usual. You should overwrite your dependency injector
20
     * with the returned mock object. You can also call `defineFor*()` on this mock to use this mock for specific
21
     * classes.
22
     *
23
     * The PDO object is mocked too. This object should not receive any calls except for quoting. By default it
24
     * accepts `quote(string)`, `setAttribute(*)` and `getAttribute(ATTR_DRIVER_NAME)`. To retrieve and expect other
25
     * calls you can use `getConnection()` from EntityManager mock object.
26
     *
27
     * @param array $options Options passed to EntityManager constructor
28
     * @param string $driver Database driver you are using (results in different dbal instance)
29
     * @return m\Mock|EntityManager
30
     */
31
    public function ormInitMock($options = [], $driver = 'mysql')
32 26
    {
33
        /** @var EntityManager|m\Mock $em */
34
        $em = m::mock(EntityManager::class)->makePartial();
35 26
        $em->__construct($options);
36
        /** @var \PDO|m\Mock $pdo */
37 26
        $pdo = m::mock(\PDO::class);
38
39 26
        $pdo->shouldReceive('setAttribute')->andReturn(true)->byDefault();
40 26
        $pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_DRIVER_NAME)->andReturn($driver)->byDefault();
41 26
        $pdo->shouldReceive('quote')->with(m::type('string'))->andReturnUsing(
42 26
            function ($str) {
43 1
                return '\'' . addcslashes($str, '\'') . '\'';
44 26
            }
45 26
        )->byDefault();
46
47 26
        $em->setConnection($pdo);
48 26
        return $em;
49
    }
50
51
    /**
52
     * Create a partial mock of Entity $class
53
     *
54
     * @param string        $class
55
     * @param array         $data
56
     * @param EntityManager $em
57
     * @return m\Mock|Entity
58
     */
59 10
    public function ormCreateMockedEntity($class, $data = [], $em = null)
60
    {
61 10
        $em = $em ?: EntityManager::getInstance($class);
62
63
        /** @var Entity|m\Mock $entity */
64 10
        $entity = m::mock($class)->makePartial();
65 10
        $entity->setEntityManager($em);
66 10
        $entity->setOriginalData($data);
67 10
        $entity->reset();
68
69
        try {
70 10
            /** @scrutinizer ignore-type */
71 1
            $em->map($entity, true, $class);
0 ignored issues
show
Bug introduced by
$entity of type Mockery\Mock is incompatible with the type ORM\Entity expected by parameter $entity of ORM\EntityManager::map(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
            $em->map(/** @scrutinizer ignore-type */ $entity, true, $class);
Loading history...
72
        } catch (IncompletePrimaryKey $ex) {
73
            // we tried to map but ignore primary key missing
74 10
        }
75
        return $entity;
76
    }
77
78
    /**
79
     * Expect an insert for $class
80
     *
81
     * Mocks and expects the calls to sync and insert as they came for `save()` method for a new Entity.
82
     *
83
     * If you omit the auto incremented id in defaultValues it is set to a random value between 1 and 2147483647.
84
     *
85
     * The EntityManager gets determined the same way as in Entity and can be overwritten by third parameter here.
86
     *
87
     * @param string        $class         The class that should get created
88
     * @param array         $defaultValues The default values that came from database (for example: the created column
89
     *                                     has by the default the current timestamp; the id is auto incremented...)
90
     * @param EntityManager $em
91
     */
92 5
    public function ormExpectInsert($class, $defaultValues = [], $em = null)
93
    {
94
        $expectation = $this->ormAllowInsert($class, $defaultValues, $em);
95 5
        $expectation->once();
96
    }
97 5
98 5
    /**
99 5
     * Allow an insert for $class
100 4
     *
101 4
     * Mocks the calls to sync and insert as they came for `save()` method for a new Entity.
102 4
     *
103 4
     * If you omit the auto incremented id in defaultValues it is set to a random value between 1 and 2147483647.
104 1
     *
105
     * The EntityManager gets determined the same way as in Entity and can be overwritten by third parameter here.
106 4
     *
107 4
     * @param string        $class         The class that should get created
108 4
     * @param array         $defaultValues The default values that came from database (for example: the created column
109 4
     *                                     has by the default the current timestamp; the id is auto incremented...)
110 4
     * @param EntityManager $em
111
     * @return m\Expectation
112
     */
113
    public function ormAllowInsert($class, $defaultValues = [], $em = null)
114 4
    {
115 1
        /** @var EntityManager|m\Mock $em */
116 1
        $em = $em ?: EntityManager::getInstance($class);
117 3
118 3
        /** @scrutinizer ignore-call */
119 3
        $expectation = $em->shouldReceive('sync')->with(m::type($class))
0 ignored issues
show
Bug introduced by
The method shouldReceive() does not exist on ORM\EntityManager. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        $expectation = $em->/** @scrutinizer ignore-call */ shouldReceive('sync')->with(m::type($class))

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
120
            ->andReturnUsing(
121 5
                function (Entity $entity) use ($class, $defaultValues, $em) {
122
                    /** @scrutinizer ignore-call */
123 5
                    $expectation = $em->shouldReceive('insert')->once()
124
                        ->andReturnUsing(
125
                            function (Entity $entity, $useAutoIncrement = true) use ($defaultValues, $em) {
126
                                if ($useAutoIncrement && !isset($defaultValues[$entity::getPrimaryKeyVars()[0]])) {
127
                                    $defaultValues[$entity::getPrimaryKeyVars()[0]] = mt_rand(1, pow(2, 31) - 1);
128
                                }
129
                                $entity->setOriginalData(array_merge($defaultValues, $entity->getData()));
130
                                $entity->reset();
131
                                $em->map($entity);
132
                                return true;
133
                            }
134
                        );
135
136 6
                    try {
137
                        $entity->getPrimaryKey();
138
                        $expectation->with(m::type($class), false);
139 6
                        return false;
140
                    } catch (IncompletePrimaryKey $ex) {
141
                        $expectation->with(m::type($class));
142 6
                        throw $ex;
143 6
                    }
144
                }
145 6
            );
146 6
147 6
        return $expectation;
148
    }
149 6
150
    /**
151
     * Expect fetch for $class
152
     *
153
     * Mocks and expects an EntityFetcher with $entities as result.
154
     *
155
     * @param string        $class    The class that should be fetched
156
     * @param array         $entities The entities that get returned from fetcher
157
     * @param EntityManager $em
158
     * @return m\Mock|EntityFetcher
159
     */
160
    public function ormExpectFetch($class, $entities = [], $em = null)
161 4
    {
162
        list($expectation, $fetcher) = $this->ormAllowFetch($class, $entities, $em);
163 4
        $expectation->once();
164 4
        return $fetcher;
165
    }
166 3
167 1
    /**
168 1
     * Allow fetch for $class
169 1
     *
170 1
     * Mocks an EntityFetcher with $entities as result.
171
     *
172
     * Returns the Expectation for fetch on entityManager and the mocked EntityFetcher
173 3
     *
174 2
     * @param string        $class    The class that should be fetched
175
     * @param array         $entities The entities that get returned from fetcher
176
     * @param EntityManager $em
177
     * @return m\Expectation|EntityFetcher|m\Mock[]
178 1
     */
179 1
    public function ormAllowFetch($class, $entities = [], $em = null)
180 1
    {
181 1
        /** @var EntityManager|m\Mock $em */
182
        $em = $em ?: EntityManager::getInstance($class);
183 1
184 4
        /** @var m\Mock|EntityFetcher $fetcher */
185
        $fetcher = m::mock(EntityFetcher::class, [ $em, $class ])->makePartial();
186 4
        $expectation = $em->shouldReceive('fetch')->with($class)->andReturn($fetcher);
187
188
        /** @scrutinizer ignore-call */
189
        $fetcher->shouldReceive('count')->with()->andReturn(count($entities))->byDefault();
190
        array_push($entities, null);
191
        $fetcher->shouldReceive('one')->with()->andReturnValues($entities)->byDefault();
192
193
        return [$expectation, $fetcher];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($expectation, $fetcher) returns an array which contains values of type Mockery\Expectation which are incompatible with the documented value type Mockery\Mock.
Loading history...
194
    }
195
196
    /**
197
     * Expect save on $entity
198 2
     *
199
     * Entity has to be a mock use `emCreateMockedEntity()` to create it.
200 2
     *
201
     * @param Entity|m\Mock $entity
202
     * @param array  $changingData Emulate changing data during update statement (triggers etc)
203 2
     * @param array  $updatedData  Emulate data changes in database
204
     */
205 2
    public function ormExpectUpdate(Entity $entity, $changingData = [], $updatedData = [])
206 2
    {
207 1
        $expectation = $this->ormAllowUpdate($entity, $changingData, $updatedData);
208
        $expectation->once();
209 1
    }
210
211 2
    /**
212 2
     * Allow save on $entity
213 2
     *
214 2
     * Entity has to be a mock use `emCreateMockedEntity()` to create it.
215 2
     *
216
     * @param Entity|m\Mock $entity
217
     * @param array $changingData Emulate changing data during update statement (triggers etc)
218
     * @param array $updatedData Emulate data changes in database
219
     * @return m\Expectation
220
     */
221
    public function ormAllowUpdate(Entity $entity, $changingData = [], $updatedData = [])
222
    {
223
        /** @scrutinizer ignore-call */
224
        $expectation = $entity->shouldReceive('save')->andReturnUsing(
0 ignored issues
show
Bug introduced by
The method shouldReceive() does not exist on ORM\Entity. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

224
        $expectation = $entity->/** @scrutinizer ignore-call */ shouldReceive('save')->andReturnUsing(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
225
            function () use ($entity, $updatedData, $changingData) {
226
                // sync with database using $updatedData
227
                if (!empty($updatedData)) {
228
                    $newData = $entity->getData();
229
                    $entity->reset();
230
                    $entity->setOriginalData(array_merge($entity->getData(), $updatedData));
231
                    $entity->fill($newData);
232
                }
233
234
                if (!$entity->isDirty()) {
235
                    return $entity;
236
                }
237
238
                // update the entity using $changingData
239
                $entity->preUpdate();
240
                $entity->setOriginalData(array_merge($entity->getData(), $changingData));
241
                $entity->reset();
242
                $entity->postUpdate();
243
244
                return $entity;
245
            }
246
        );
247
248
        return $expectation;
249
    }
250
251
    /**
252
     * Expect delete on $em
253
     *
254
     * If $em is not given it is determined by get_class($entity).
255
     *
256
     * If $entity is a string then it is assumed to be a class name.
257
     *
258
     * @param string|Entity $entity
259
     * @param EntityManager $em
260
     */
261
    public function ormExpectDelete($entity, $em = null)
262
    {
263
        $expectation = $this->ormAllowDelete($entity, $em);
264
        $expectation->once();
265
    }
266
267
    /**
268
     * Allow delete on $em
269
     *
270
     * If $em is not given it is determined by get_class($entity).
271
     *
272
     * If $entity is a string then it is assumed to be a class name.
273
     *
274
     * @param string|Entity $entity
275
     * @param EntityManager $em
276
     * @return m\Expectation
277
     */
278
    public function ormAllowDelete($entity, $em = null)
279
    {
280
        $class = is_string($entity) ? $entity : get_class($entity);
281
282
        /** @var EntityManager|m\Mock $em */
283
        $em = $em ?: EntityManager::getInstance($class);
284
285
        $expectation = $em->shouldReceive('delete');
286
        if (is_string($entity)) {
287
            $expectation->with(m::type($class));
288
        } else {
289
            $expectation->with($entity);
290
        }
291
        $expectation->once()->andReturnUsing(
292
            function (Entity $entity) {
293
                $entity->setOriginalData([]);
294
                return true;
295
            }
296
        );
297
298
        return $expectation;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $expectation returns the type Mockery\HigherOrderMessage which is incompatible with the documented return type Mockery\Expectation.
Loading history...
299
    }
300
}
301