Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
.. index::
2
    single: Cookbook; Mocking Hard Dependencies
3
 
4
Mocking Hard Dependencies (new Keyword)
5
=======================================
6
 
7
One prerequisite to mock hard dependencies is that the code we are trying to test uses autoloading.
8
 
9
Let's take the following code for an example:
10
 
11
.. code-block:: php
12
 
13
    <?php
14
    namespace App;
15
    class Service
16
    {
17
        function callExternalService($param)
18
        {
19
            $externalService = new Service\External($version = 5);
20
            $externalService->sendSomething($param);
21
            return $externalService->getSomething();
22
        }
23
    }
24
 
25
The way we can test this without doing any changes to the code itself is by creating :doc:`instance mocks </reference/instance_mocking>` by using the ``overload`` prefix.
26
 
27
.. code-block:: php
28
 
29
    <?php
30
    namespace AppTest;
31
    use Mockery as m;
32
    class ServiceTest extends \PHPUnit_Framework_TestCase
33
    {
34
        public function testCallingExternalService()
35
        {
36
            $param = 'Testing';
37
 
38
            $externalMock = m::mock('overload:App\Service\External');
39
            $externalMock->shouldReceive('sendSomething')
40
                ->once()
41
                ->with($param);
42
            $externalMock->shouldReceive('getSomething')
43
                ->once()
44
                ->andReturn('Tested!');
45
 
46
            $service = new \App\Service();
47
 
48
            $result = $service->callExternalService($param);
49
 
50
            $this->assertSame('Tested!', $result);
51
        }
52
    }
53
 
54
If we run this test now, it should pass. Mockery does its job and our ``App\Service`` will use the mocked external service instead of the real one.
55
 
56
The problem with this is when we want to, for example, test the ``App\Service\External`` itself, or if we use that class somewhere else in our tests.
57
 
58
When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a "class already exists" exception. This is where autoloading kicks in and makes our job a lot easier.
59
 
60
To make this possible, we'll tell PHPUnit to run the tests that have overloaded classes in separate processes and to not preserve global state. That way we'll avoid having the overloaded class included more than once. Of course this has its downsides as these tests will run slower.
61
 
62
Our test example from above now becomes:
63
 
64
.. code-block:: php
65
 
66
    <?php
67
    namespace AppTest;
68
    use Mockery as m;
69
    /**
70
     * @runTestsInSeparateProcesses
71
     * @preserveGlobalState disabled
72
     */
73
    class ServiceTest extends \PHPUnit_Framework_TestCase
74
    {
75
        public function testCallingExternalService()
76
        {
77
            $param = 'Testing';
78
 
79
            $externalMock = m::mock('overload:App\Service\External');
80
            $externalMock->shouldReceive('sendSomething')
81
                ->once()
82
                ->with($param);
83
            $externalMock->shouldReceive('getSomething')
84
                ->once()
85
                ->andReturn('Tested!');
86
 
87
            $service = new \App\Service();
88
 
89
            $result = $service->callExternalService($param);
90
 
91
            $this->assertSame('Tested!', $result);
92
        }
93
    }
94
 
95
 
96
 
97
Testing the constructor arguments of hard Dependencies
98
------------------------------------------------------
99
 
100
Sometimes we might want to ensure that the hard dependency is instantiated with
101
particular arguments. With overloaded mocks, we can set up expectations on the
102
constructor.
103
 
104
.. code-block:: php
105
 
106
    <?php
107
    namespace AppTest;
108
    use Mockery as m;
109
    /**
110
     * @runTestsInSeparateProcesses
111
     * @preserveGlobalState disabled
112
     */
113
    class ServiceTest extends \PHPUnit_Framework_TestCase
114
    {
115
        public function testCallingExternalService()
116
        {
117
            $externalMock = m::mock('overload:App\Service\External');
118
            $externalMock->allows('sendSomething');
119
            $externalMock->shouldReceive('__construct')
120
                ->once()
121
                ->with(5);
122
 
123
            $service = new \App\Service();
124
            $result = $service->callExternalService($param);
125
        }
126
    }
127
 
128
 
129
.. note::
130
    For more straightforward and single-process tests oriented way check
131
    :ref:`mocking-class-within-class`.
132
 
133
.. note::
134
 
135
    This cookbook entry is an adaption of the blog post titled
136
    `"Mocking hard dependencies with Mockery" <https://robertbasic.com/blog/mocking-hard-dependencies-with-mockery/>`_,
137
    published by Robert Basic on his blog.