Passed
Pull Request — master (#41)
by Thomas
03:05
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 23
    public function ormInitMock($options = [], $driver = 'mysql')
28
    {
29
        /** @var EntityManager|m\MockInterface $em */
30 23
        $em = m::mock(EntityManager::class, [$options])->makePartial();
31
        /** @var \PDO|m\MockInterface $pdo */
32 23
        $pdo = m::mock(\PDO::class);
33
34 23
        $pdo->shouldReceive('setAttribute')->andReturn(true)->byDefault();
1 ignored issue
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in PDO.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
35 23
        $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 23
        })->byDefault();
39
40 23
        $em->setConnection($pdo);
41 23
        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 7
    public function ormCreateMockedEntity($class, $data = [], $em = null)
53
    {
54 7
        $em = $em ?: EntityManager::getInstance($class);
55
56
        /** @var Entity|m\MockInterface $entity */
57 7
        $entity = m::mock($class)->makePartial();
58 7
        $entity->setEntityManager($em);
59 7
        $entity->setOriginalData($data);
60 7
        $entity->reset();
61
62
        try {
63 7
            $em->map($entity, true, $class);
1 ignored issue
show
Bug introduced by
It seems like $entity defined by \Mockery::mock($class)->makePartial() on line 57 can also be of type object<Mockery\MockInterface>; however, ORM\EntityManager::map() does only seem to accept object<ORM\Entity>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
64 1
        } catch (IncompletePrimaryKey $ex) {
65
            // we tried to map but ignore primary key missing
66
        }
67 7
        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()
1 ignored issue
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in ORM\EntityManager.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
91
            ->andReturnUsing(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...
92 4
                $expectation = $em->shouldReceive('insert')->once()
1 ignored issue
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in ORM\EntityManager.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
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 5
    public function ormExpectFetch($class, $entities = [], $em = null)
126
    {
127
        /** @var EntityManager|m\MockInterface $em */
128 5
        $em = $em ?: EntityManager::getInstance($class);
129
130
        /** @var m\MockInterface|EntityFetcher $fetcher */
131 5
        $fetcher = m::mock(EntityFetcher::class, [$em, $class])->makePartial();
132 5
        $em->shouldReceive('fetch')->with($class)->once()->andReturn($fetcher);
1 ignored issue
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in ORM\EntityManager.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
133
134 5
        $fetcher->shouldReceive('count')->with()->andReturn(count($entities))->byDefault();
1 ignored issue
show
Bug introduced by
The method shouldReceive does only exist in Mockery\MockInterface, but not in ORM\EntityFetcher.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
135 5
        array_push($entities, null);
136 5
        $fetcher->shouldReceive('one')->with()->andReturnValues($entities)->byDefault();
137
138 5
        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
    public function ormExpectUpdate(Entity $entity, $changingData = [], $updatedData = [])
151
    {
152 4
        $entity->shouldReceive('save')->once()->andReturnUsing(function () use ($entity, $updatedData, $changingData) {
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...
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