Passed
Pull Request — master (#41)
by Thomas
03:15
created

MockTrait::ormExpectInsert()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 28
ccs 19
cts 19
cp 1
rs 8.439
cc 5
eloc 19
nc 2
nop 3
crap 5
1
<?php
2
3
namespace ORM;
4
5
use Mockery as m;
6
use Mockery\Mock;
7
use ORM\Exception\IncompletePrimaryKey;
8
9
trait MockTrait
10
{
11
12
    /**
13
     * Initialize an EntityManager mock object
14
     *
15
     * The mock is partial and you can map and act with it as usual. You should overwrite your dependency injector
16
     * with the returned mock object. You can also call `defineFor*()` on this mock to use this mock for specific
17
     * classes.
18
     *
19
     * The PDO object is mocked too. This object should not receive any calls except for quoting. By default it
20
     * accepts `quote(string)`, `setAttribute(*)` and `getAttribute(ATTR_DRIVER_NAME)`. To retrieve and expect other
21
     * calls you can use `getConnection()` from EntityManager mock object.
22
     *
23
     * @param array  $options Options passed to EntityManager constructor
24
     * @param string $driver  Database driver you are using (results in different dbal instance)
25
     * @return m\MockInterface|EntityManager
26
     */
27 26
    public function ormInitMock($options = [], $driver = 'mysql')
28
    {
29
        /** @var EntityManager|m\MockInterface $em */
30 26
        $em = m::mock(EntityManager::class, [$options])->makePartial();
31
        /** @var \PDO|m\MockInterface $pdo */
32 26
        $pdo = m::mock(\PDO::class);
33
34 26
        $pdo->shouldReceive('setAttribute')->andReturn(true)->byDefault();
35 26
        $pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_DRIVER_NAME)->andReturn($driver)->byDefault();
36
        $pdo->shouldReceive('quote')->with(stringValue())->andReturnUsing(function ($str) {
37 1
            return '\'' . addcslashes($str, '\'') . '\'';
38 26
        })->byDefault();
39
40 26
        $em->setConnection($pdo);
41 26
        return $em;
42
    }
43
44
    /**
45
     * Create a partial mock of Entity $class
46
     *
47
     * @param string        $class
48
     * @param array         $data
49
     * @param EntityManager $em
50
     * @return m\MockInterface|Entity
51
     */
52 10
    public function ormCreateMockedEntity($class, $data = [], $em = null)
53
    {
54 10
        $em = $em ?: EntityManager::getInstance($class);
55
56
        /** @var Entity|m\MockInterface $entity */
57 10
        $entity = m::mock($class)->makePartial();
58 10
        $entity->setEntityManager($em);
59 10
        $entity->setOriginalData($data);
60 10
        $entity->reset();
61
62
        try {
63 10
            $em->map($entity, true, $class);
64 1
        } catch (IncompletePrimaryKey $ex) {
65
            // we tried to map but ignore primary key missing
66
        }
67 10
        return $entity;
68
    }
69
70
    /**
71
     * Expect an insert for $class
72
     *
73
     * Mocks and expects the calls to sync and insert as they came for `save()` method for a new Entity.
74
     *
75
     * If you omit the auto incremented id in defaultValues it is set to a random value between 1 and 2147483647.
76
     *
77
     * The EntityManager gets determined the same way as in Entity and can be overwritten by third parameter here.
78
     *
79
     * @param string        $class         The class that should get created
80
     * @param array         $defaultValues The default values that came from database (for example: the created column
81
     *                                     has by the default the current timestamp; the id is auto incremented...)
82
     * @param EntityManager $em
83
     * @throws Exception
84
     */
85 5
    public function ormExpectInsert($class, $defaultValues = [], $em = null)
86
    {
87
        /** @var EntityManager|m\MockInterface $em */
88 5
        $em = $em ?: EntityManager::getInstance($class);
89
90 5
        $em->shouldReceive('sync')->with(m::type($class))->once()
91
            ->andReturnUsing(function (Entity $entity, $reset = false) use ($class, $defaultValues, $em) {
92 4
                $expectation = $em->shouldReceive('insert')->once()
93
                    ->andReturnUsing(function (Entity $entity, $useAutoIncrement = true) use ($defaultValues, $em) {
94 4
                        if ($useAutoIncrement && !isset($defaultValues[$entity::getPrimaryKeyVars()[0]])) {
95 1
                            $defaultValues[$entity::getPrimaryKeyVars()[0]] = mt_rand(1, pow(2, 31) - 1);
96
                        }
97 4
                        $entity->setOriginalData(array_merge($defaultValues, $entity->getData()));
98 4
                        $entity->reset();
99 4
                        $em->map($entity);
100 4
                        return true;
101 4
                    });
102
103
                try {
104 4
                    $entity->getPrimaryKey();
105 1
                    $expectation->with(m::type($class), false);
106 1
                    return false;
107 3
                } catch (IncompletePrimaryKey $ex) {
108 3
                    $expectation->with(m::type($class));
109 3
                    throw $ex;
110
                }
111 5
            });
112 5
    }
113
114
    /**
115
     * Expect fetch for $class
116
     *
117
     * Mocks and expects an EntityFetcher with $entities as result.
118
     *
119
     * @param string        $class    The class that should be fetched
120
     * @param array         $entities The entities that get returned from fetcher
121
     * @param EntityManager $em
122
     * @return m\MockInterface|EntityFetcher
123
     * @throws Exception
124
     */
125 6
    public function ormExpectFetch($class, $entities = [], $em = null)
126
    {
127
        /** @var EntityManager|m\MockInterface $em */
128 6
        $em = $em ?: EntityManager::getInstance($class);
129
130
        /** @var m\MockInterface|EntityFetcher $fetcher */
131 6
        $fetcher = m::mock(EntityFetcher::class, [$em, $class])->makePartial();
132 6
        $em->shouldReceive('fetch')->with($class)->once()->andReturn($fetcher);
133
134 6
        $fetcher->shouldReceive('count')->with()->andReturn(count($entities))->byDefault();
135 6
        array_push($entities, null);
136 6
        $fetcher->shouldReceive('one')->with()->andReturnValues($entities)->byDefault();
137
138 6
        return $fetcher;
139
    }
140
141
    /**
142
     * Expect save on $entity
143
     *
144
     * Entity has to be a mock use `emCreateMockedEntity()` to create it.
145
     *
146
     * @param Entity $entity
147
     * @param array  $changingData Emulate changing data during update statement (triggers etc)
148
     * @param array  $updatedData  Emulate data changes in database
149
     */
150 4
    public function ormExpectUpdate(Entity $entity, $changingData = [], $updatedData = [])
151
    {
152
        $entity->shouldReceive('save')->once()->andReturnUsing(function () use ($entity, $updatedData, $changingData) {
153
            // sync with database using $updatedData
154 3
            if (!empty($updatedData)) {
155 1
                $newData = $entity->getData();
156 1
                $entity->reset();
157 1
                $entity->setOriginalData(array_merge($entity->getData(), $updatedData));
158 1
                $entity->fill($newData);
159
            }
160
161 3
            if (!$entity->isDirty()) {
162 2
                return $entity;
163
            }
164
165
            // update the entity using $changingData
166 1
            $entity->preUpdate();
167 1
            $entity->setOriginalData(array_merge($entity->getData(), $changingData));
168 1
            $entity->reset();
169 1
            $entity->postUpdate();
170
171 1
            return $entity;
172 4
        });
173 4
    }
174
175
    /**
176
     * Expect delete on $em
177
     *
178
     * If $em is not given it is determined by get_class($entity).
179
     *
180
     * If $entity is a string then it is assumed to be a class name.
181
     *
182
     * @param string|Entity $entity
183
     * @param EntityManager $em
184
     */
185 2
    public function ormExpectDelete($entity, $em = null)
186
    {
187 2
        $class = is_string($entity) ? $entity : get_class($entity);
188
189
        /** @var EntityManager|m\MockInterface $em */
190 2
        $em = $em ?: EntityManager::getInstance($class);
191
192 2
        $expectation = $em->shouldReceive('delete');
193 2
        if (is_string($entity)) {
194 1
            $expectation->with(m::type($class));
195
        } else {
196 1
            $expectation->with($entity);
197
        }
198 2
        $expectation->once()->andReturnUsing(function (Entity $entity) {
199 2
            $entity->setOriginalData([]);
200 2
            return true;
201 2
        });
202 2
    }
203
}
204