Passed
Push — master ( 050c29...4fa43a )
by Thomas
58s
created

MockTrait::ormCreateMockedEntity()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.4285
cc 3
eloc 10
nc 4
nop 3
crap 3
1
<?php
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
    /**
18
     * Initialize an EntityManager mock object
19
     *
20
     * The mock is partial and you can map and act with it as usual. You should overwrite your dependency injector
21
     * with the returned mock object. You can also call `defineFor*()` on this mock to use this mock for specific
22
     * classes.
23
     *
24
     * The PDO object is mocked too. This object should not receive any calls except for quoting. By default it
25
     * accepts `quote(string)`, `setAttribute(*)` and `getAttribute(ATTR_DRIVER_NAME)`. To retrieve and expect other
26
     * calls you can use `getConnection()` from EntityManager mock object.
27
     *
28
     * @param array  $options Options passed to EntityManager constructor
29
     * @param string $driver  Database driver you are using (results in different dbal instance)
30
     * @return m\MockInterface|EntityManager
31
     */
32 26
    public function ormInitMock($options = [], $driver = 'mysql')
33
    {
34
        /** @var EntityManager|m\MockInterface $em */
35 26
        $em = m::mock(EntityManager::class, [ $options ])->makePartial();
36
        /** @var \PDO|m\MockInterface $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(stringValue())->andReturnUsing(
42
            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\MockInterface|Entity
58
     */
59 10
    public function ormCreateMockedEntity($class, $data = [], $em = null)
60
    {
61 10
        $em = $em ?: EntityManager::getInstance($class);
62
63
        /** @var Entity|m\MockInterface $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
            $em->map($entity, true, $class);
71 1
        } catch (IncompletePrimaryKey $ex) {
72
            // we tried to map but ignore primary key missing
73
        }
74 10
        return $entity;
75
    }
76
77
    /**
78
     * Expect an insert for $class
79
     *
80
     * Mocks and expects the calls to sync and insert as they came for `save()` method for a new Entity.
81
     *
82
     * If you omit the auto incremented id in defaultValues it is set to a random value between 1 and 2147483647.
83
     *
84
     * The EntityManager gets determined the same way as in Entity and can be overwritten by third parameter here.
85
     *
86
     * @param string        $class         The class that should get created
87
     * @param array         $defaultValues The default values that came from database (for example: the created column
88
     *                                     has by the default the current timestamp; the id is auto incremented...)
89
     * @param EntityManager $em
90
     * @throws Exception
91
     */
92 5
    public function ormExpectInsert($class, $defaultValues = [], $em = null)
93
    {
94
        /** @var EntityManager|m\MockInterface $em */
95 5
        $em = $em ?: EntityManager::getInstance($class);
96
97 5
        $em->shouldReceive('sync')->with(m::type($class))->once()
98 5
            ->andReturnUsing(
99
                function (Entity $entity, $reset = false) use ($class, $defaultValues, $em) {
1 ignored issue
show
Unused Code introduced by
The parameter $reset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
100 4
                    $expectation = $em->shouldReceive('insert')->once()
101 4
                        ->andReturnUsing(
102
                            function (Entity $entity, $useAutoIncrement = true) use ($defaultValues, $em) {
103 4
                                if ($useAutoIncrement && !isset($defaultValues[$entity::getPrimaryKeyVars()[0]])) {
104 1
                                    $defaultValues[$entity::getPrimaryKeyVars()[0]] = mt_rand(1, pow(2, 31) - 1);
105
                                }
106 4
                                $entity->setOriginalData(array_merge($defaultValues, $entity->getData()));
107 4
                                $entity->reset();
108 4
                                $em->map($entity);
109 4
                                return true;
110 4
                            }
111
                        );
112
113
                    try {
114 4
                        $entity->getPrimaryKey();
115 1
                        $expectation->with(m::type($class), false);
116 1
                        return false;
117 3
                    } catch (IncompletePrimaryKey $ex) {
118 3
                        $expectation->with(m::type($class));
119 3
                        throw $ex;
120
                    }
121 5
                }
122
            );
123 5
    }
124
125
    /**
126
     * Expect fetch for $class
127
     *
128
     * Mocks and expects an EntityFetcher with $entities as result.
129
     *
130
     * @param string        $class    The class that should be fetched
131
     * @param array         $entities The entities that get returned from fetcher
132
     * @param EntityManager $em
133
     * @return m\MockInterface|EntityFetcher
134
     * @throws Exception
135
     */
136 6
    public function ormExpectFetch($class, $entities = [], $em = null)
137
    {
138
        /** @var EntityManager|m\MockInterface $em */
139 6
        $em = $em ?: EntityManager::getInstance($class);
140
141
        /** @var m\MockInterface|EntityFetcher $fetcher */
142 6
        $fetcher = m::mock(EntityFetcher::class, [ $em, $class ])->makePartial();
143 6
        $em->shouldReceive('fetch')->with($class)->once()->andReturn($fetcher);
144
145 6
        $fetcher->shouldReceive('count')->with()->andReturn(count($entities))->byDefault();
146 6
        array_push($entities, null);
147 6
        $fetcher->shouldReceive('one')->with()->andReturnValues($entities)->byDefault();
148
149 6
        return $fetcher;
150
    }
151
152
    /**
153
     * Expect save on $entity
154
     *
155
     * Entity has to be a mock use `emCreateMockedEntity()` to create it.
156
     *
157
     * @param Entity $entity
158
     * @param array  $changingData Emulate changing data during update statement (triggers etc)
159
     * @param array  $updatedData  Emulate data changes in database
160
     */
161 4
    public function ormExpectUpdate(Entity $entity, $changingData = [], $updatedData = [])
162
    {
163 4
        $entity->shouldReceive('save')->once()->andReturnUsing(
1 ignored issue
show
Bug introduced by
The method shouldReceive() does not seem to exist on object<ORM\Entity>.

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...
164
            function () use ($entity, $updatedData, $changingData) {
165
                // sync with database using $updatedData
166 3
                if (!empty($updatedData)) {
167 1
                    $newData = $entity->getData();
168 1
                    $entity->reset();
169 1
                    $entity->setOriginalData(array_merge($entity->getData(), $updatedData));
170 1
                    $entity->fill($newData);
171
                }
172
173 3
                if (!$entity->isDirty()) {
174 2
                    return $entity;
175
                }
176
177
                // update the entity using $changingData
178 1
                $entity->preUpdate();
179 1
                $entity->setOriginalData(array_merge($entity->getData(), $changingData));
180 1
                $entity->reset();
181 1
                $entity->postUpdate();
182
183 1
                return $entity;
184 4
            }
185
        );
186 4
    }
187
188
    /**
189
     * Expect delete on $em
190
     *
191
     * If $em is not given it is determined by get_class($entity).
192
     *
193
     * If $entity is a string then it is assumed to be a class name.
194
     *
195
     * @param string|Entity $entity
196
     * @param EntityManager $em
197
     */
198 2
    public function ormExpectDelete($entity, $em = null)
199
    {
200 2
        $class = is_string($entity) ? $entity : get_class($entity);
201
202
        /** @var EntityManager|m\MockInterface $em */
203 2
        $em = $em ?: EntityManager::getInstance($class);
204
205 2
        $expectation = $em->shouldReceive('delete');
206 2
        if (is_string($entity)) {
207 1
            $expectation->with(m::type($class));
208
        } else {
209 1
            $expectation->with($entity);
210
        }
211 2
        $expectation->once()->andReturnUsing(
212 2
            function (Entity $entity) {
213 2
                $entity->setOriginalData([]);
214 2
                return true;
215 2
            }
216
        );
217 2
    }
218
}
219