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; Class Constants
3
 
4
Class Constants
5
===============
6
 
7
When creating a test double for a class, Mockery does not create stubs out of
8
any class constants defined in the class we are mocking. Sometimes though, the
9
non-existence of these class constants, setup of the test, and the application
10
code itself, it can lead to undesired behavior, and even a PHP error:
11
``PHP Fatal error:  Uncaught Error: Undefined class constant 'FOO' in ...``
12
 
13
While supporting class constants in Mockery would be possible, it does require
14
an awful lot of work, for a small number of use cases.
15
 
16
Named Mocks
17
-----------
18
 
19
We can, however, deal with these constants in a way supported by Mockery - by
20
using :ref:`creating-test-doubles-named-mocks`.
21
 
22
A named mock is a test double that has a name of the class we want to mock, but
23
under it is a stubbed out class that mimics the real class with canned responses.
24
 
25
Lets look at the following made up, but not impossible scenario:
26
 
27
.. code-block:: php
28
 
29
    class Fetcher
30
    {
31
        const SUCCESS = 0;
32
        const FAILURE = 1;
33
 
34
        public static function fetch()
35
        {
36
            // Fetcher gets something for us from somewhere...
37
            return self::SUCCESS;
38
        }
39
    }
40
 
41
    class MyClass
42
    {
43
        public function doFetching()
44
        {
45
            $response = Fetcher::fetch();
46
 
47
            if ($response == Fetcher::SUCCESS) {
48
                echo "Thanks!" . PHP_EOL;
49
            } else {
50
                echo "Try again!" . PHP_EOL;
51
            }
52
        }
53
    }
54
 
55
Our ``MyClass`` calls a ``Fetcher`` that fetches some resource from somewhere -
56
maybe it downloads a file from a remote web service. Our ``MyClass`` prints out
57
a response message depending on the response from the ``Fetcher::fetch()`` call.
58
 
59
When testing ``MyClass`` we don't really want ``Fetcher`` to go and download
60
random stuff from the internet every time we run our test suite. So we mock it
61
out:
62
 
63
.. code-block:: php
64
 
65
    // Using alias: because fetch is called statically!
66
    \Mockery::mock('alias:Fetcher')
67
        ->shouldReceive('fetch')
68
        ->andReturn(0);
69
 
70
    $myClass = new MyClass();
71
    $myClass->doFetching();
72
 
73
If we run this, our test will error out with a nasty
74
``PHP Fatal error:  Uncaught Error: Undefined class constant 'SUCCESS' in ..``.
75
 
76
Here's how a ``namedMock()`` can help us in a situation like this.
77
 
78
We create a stub for the ``Fetcher`` class, stubbing out the class constants,
79
and then use ``namedMock()`` to create a mock named ``Fetcher`` based on our
80
stub:
81
 
82
.. code-block:: php
83
 
84
    class FetcherStub
85
    {
86
        const SUCCESS = 0;
87
        const FAILURE = 1;
88
    }
89
 
90
    \Mockery::namedMock('Fetcher', 'FetcherStub')
91
        ->shouldReceive('fetch')
92
        ->andReturn(0);
93
 
94
    $myClass = new MyClass();
95
    $myClass->doFetching();
96
 
97
This works because under the hood, Mockery creates a class called ``Fetcher``
98
that extends ``FetcherStub``.
99
 
100
The same approach will work even if ``Fetcher::fetch()`` is not a static
101
dependency:
102
 
103
.. code-block:: php
104
 
105
    class Fetcher
106
    {
107
        const SUCCESS = 0;
108
        const FAILURE = 1;
109
 
110
        public function fetch()
111
        {
112
            // Fetcher gets something for us from somewhere...
113
            return self::SUCCESS;
114
        }
115
    }
116
 
117
    class MyClass
118
    {
119
        public function doFetching($fetcher)
120
        {
121
            $response = $fetcher->fetch();
122
 
123
            if ($response == Fetcher::SUCCESS) {
124
                echo "Thanks!" . PHP_EOL;
125
            } else {
126
                echo "Try again!" . PHP_EOL;
127
            }
128
        }
129
    }
130
 
131
And the test will have something like this:
132
 
133
.. code-block:: php
134
 
135
    class FetcherStub
136
    {
137
        const SUCCESS = 0;
138
        const FAILURE = 1;
139
    }
140
 
141
    $mock = \Mockery::mock('Fetcher', 'FetcherStub')
142
    $mock->shouldReceive('fetch')
143
        ->andReturn(0);
144
 
145
    $myClass = new MyClass();
146
    $myClass->doFetching($mock);
147
 
148
 
149
Constants Map
150
-------------
151
 
152
Another way of mocking class constants can be with the use of the constants map configuration.
153
 
154
Given a class with constants:
155
 
156
.. code-block:: php
157
 
158
    class Fetcher
159
    {
160
        const SUCCESS = 0;
161
        const FAILURE = 1;
162
 
163
        public function fetch()
164
        {
165
            // Fetcher gets something for us from somewhere...
166
            return self::SUCCESS;
167
        }
168
    }
169
 
170
It can be mocked with:
171
 
172
.. code-block:: php
173
 
174
    \Mockery::getConfiguration()->setConstantsMap([
175
        'Fetcher' => [
176
            'SUCCESS' => 'success',
177
            'FAILURE' => 'fail',
178
        ]
179
    ]);
180
 
181
    $mock = \Mockery::mock('Fetcher');
182
    var_dump($mock::SUCCESS); // (string) 'success'
183
    var_dump($mock::FAILURE); // (string) 'fail'