| 1 |
lars |
1 |
<html>
|
|
|
2 |
<head>
|
|
|
3 |
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
|
4 |
<title>SimpleTest for PHP server stubs documentation</title>
|
|
|
5 |
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
|
|
|
6 |
</head>
|
|
|
7 |
<body>
|
|
|
8 |
<div class="menu_back">
|
|
|
9 |
<div class="menu">
|
|
|
10 |
<h2>
|
|
|
11 |
<a href="index.html">SimpleTest</a>
|
|
|
12 |
</h2>
|
|
|
13 |
<ul>
|
|
|
14 |
<li>
|
|
|
15 |
<a href="overview.html">Overview</a>
|
|
|
16 |
</li>
|
|
|
17 |
<li>
|
|
|
18 |
<a href="unit_test_documentation.html">Unit tester</a>
|
|
|
19 |
</li>
|
|
|
20 |
<li>
|
|
|
21 |
<a href="group_test_documentation.html">Group tests</a>
|
|
|
22 |
</li>
|
|
|
23 |
<li>
|
|
|
24 |
<span class="chosen">Server stubs</span>
|
|
|
25 |
</li>
|
|
|
26 |
<li>
|
|
|
27 |
<a href="mock_objects_documentation.html">Mock objects</a>
|
|
|
28 |
</li>
|
|
|
29 |
<li>
|
|
|
30 |
<a href="partial_mocks_documentation.html">Partial mocks</a>
|
|
|
31 |
</li>
|
|
|
32 |
<li>
|
|
|
33 |
<a href="reporter_documentation.html">Reporting</a>
|
|
|
34 |
</li>
|
|
|
35 |
<li>
|
|
|
36 |
<a href="expectation_documentation.html">Expectations</a>
|
|
|
37 |
</li>
|
|
|
38 |
<li>
|
|
|
39 |
<a href="web_tester_documentation.html">Web tester</a>
|
|
|
40 |
</li>
|
|
|
41 |
<li>
|
|
|
42 |
<a href="form_testing_documentation.html">Testing forms</a>
|
|
|
43 |
</li>
|
|
|
44 |
<li>
|
|
|
45 |
<a href="authentication_documentation.html">Authentication</a>
|
|
|
46 |
</li>
|
|
|
47 |
<li>
|
|
|
48 |
<a href="browser_documentation.html">Scriptable browser</a>
|
|
|
49 |
</li>
|
|
|
50 |
</ul>
|
|
|
51 |
</div>
|
|
|
52 |
</div>
|
|
|
53 |
<h1>Server stubs documentation</h1>
|
|
|
54 |
<div class="content">
|
|
|
55 |
<p>
|
|
|
56 |
<a class="target" name="what">
|
|
|
57 |
<h2>What are server stubs?</h2>
|
|
|
58 |
</a>
|
|
|
59 |
</p>
|
|
|
60 |
<p>
|
|
|
61 |
This was originally a pattern named by Robert Binder (Testing
|
|
|
62 |
object-oriented systems: models, patterns, and tools,
|
|
|
63 |
Addison-Wesley) in 1999.
|
|
|
64 |
A server stub is a simulation of an object or component.
|
|
|
65 |
It should exactly replace a component in a system for test
|
|
|
66 |
or prototyping purposes, but remain lightweight.
|
|
|
67 |
This allows tests to run more quickly, or if the simulated
|
|
|
68 |
class has not been written, to run at all.
|
|
|
69 |
</p>
|
|
|
70 |
|
|
|
71 |
<p>
|
|
|
72 |
<a class="target" name="creation">
|
|
|
73 |
<h2>Creating server stubs</h2>
|
|
|
74 |
</a>
|
|
|
75 |
</p>
|
|
|
76 |
<p>
|
|
|
77 |
All we need is an existing class, say a database connection
|
|
|
78 |
that looks like this...
|
|
|
79 |
<pre>
|
|
|
80 |
<strong>class DatabaseConnection {
|
|
|
81 |
function DatabaseConnection() {
|
|
|
82 |
}
|
|
|
83 |
|
|
|
84 |
function query() {
|
|
|
85 |
}
|
|
|
86 |
|
|
|
87 |
function selectQuery() {
|
|
|
88 |
}
|
|
|
89 |
}</strong>
|
|
|
90 |
</pre>
|
|
|
91 |
The class does not need to have been implemented yet.
|
|
|
92 |
To create a stub version of the class we need to include the
|
|
|
93 |
server stub library and run the generator...
|
|
|
94 |
<pre>
|
|
|
95 |
<strong>require_once('simpletest/mock_objects.php');
|
|
|
96 |
require_once('database_connection.php');
|
|
|
97 |
Stub::generate('DatabaseConnection');</strong>
|
|
|
98 |
</pre>
|
|
|
99 |
This generates a clone class called
|
|
|
100 |
<span class="new_code">StubDatabaseConnection</span>.
|
|
|
101 |
We can now create instances of the new class within
|
|
|
102 |
our prototype script...
|
|
|
103 |
<pre>
|
|
|
104 |
require_once('simpletest/mock_objects.php');
|
|
|
105 |
require_once('database_connection.php');
|
|
|
106 |
Stub::generate('DatabaseConnection');
|
|
|
107 |
<strong>
|
|
|
108 |
$connection = new StubDatabaseConnection();
|
|
|
109 |
</strong>
|
|
|
110 |
</pre>
|
|
|
111 |
The stub version of a class has all the methods of the original
|
|
|
112 |
so that operations like
|
|
|
113 |
<span class="new_code">$connection->query()</span> are still
|
|
|
114 |
legal.
|
|
|
115 |
The return value will be <span class="new_code">null</span>,
|
|
|
116 |
but we can change that with...
|
|
|
117 |
<pre>
|
|
|
118 |
<strong>$connection->setReturnValue('query', 37)</strong>
|
|
|
119 |
</pre>
|
|
|
120 |
Now every time we call
|
|
|
121 |
<span class="new_code">$connection->query()</span> we get
|
|
|
122 |
the result of 37.
|
|
|
123 |
We can set the return value to anything, say a hash of
|
|
|
124 |
imaginary database results or a list of persistent objects.
|
|
|
125 |
Parameters are irrelevant here, we always get the same
|
|
|
126 |
values back each time once they have been set up this way.
|
|
|
127 |
That may not sound like a convincing replica of a
|
|
|
128 |
database connection, but for the half a dozen lines of
|
|
|
129 |
a test method it is usually all you need.
|
|
|
130 |
</p>
|
|
|
131 |
|
|
|
132 |
<p>
|
|
|
133 |
<a class="target" name="patterns">
|
|
|
134 |
<h2>Simulation patterns</h2>
|
|
|
135 |
</a>
|
|
|
136 |
</p>
|
|
|
137 |
<p>
|
|
|
138 |
Things aren't always that simple though.
|
|
|
139 |
One common problem is iterators, where constantly returning
|
|
|
140 |
the same value could cause an endless loop in the object
|
|
|
141 |
being tested.
|
|
|
142 |
For these we need to set up sequences of values.
|
|
|
143 |
Let's say we have a simple iterator that looks like this...
|
|
|
144 |
<pre>
|
|
|
145 |
class Iterator {
|
|
|
146 |
function Iterator() {
|
|
|
147 |
}
|
|
|
148 |
|
|
|
149 |
function next() {
|
|
|
150 |
}
|
|
|
151 |
}
|
|
|
152 |
</pre>
|
|
|
153 |
This is about the simplest iterator you could have.
|
|
|
154 |
Assuming that this iterator only returns text until it
|
|
|
155 |
reaches the end, when it returns false, we can simulate it
|
|
|
156 |
with...
|
|
|
157 |
<pre>
|
|
|
158 |
<strong>Stub::generate('Iterator');
|
|
|
159 |
|
|
|
160 |
$iterator = new StubIterator();
|
|
|
161 |
$iterator->setReturnValue('next', false);
|
|
|
162 |
$iterator->setReturnValueAt(0, 'next', 'First string');
|
|
|
163 |
$iterator->setReturnValueAt(1, 'next', 'Second string');</strong>
|
|
|
164 |
</pre>
|
|
|
165 |
When <span class="new_code">next()</span> is called on the
|
|
|
166 |
stub iterator it will first return "First string",
|
|
|
167 |
on the second call "Second string" will be returned
|
|
|
168 |
and on any other call <span class="new_code">false</span> will
|
|
|
169 |
be returned.
|
|
|
170 |
The sequenced return values take precedence over the constant
|
|
|
171 |
return value.
|
|
|
172 |
The constant one is a kind of default if you like.
|
|
|
173 |
</p>
|
|
|
174 |
<p>
|
|
|
175 |
Another tricky situation is an overloaded
|
|
|
176 |
<span class="new_code">get()</span> operation.
|
|
|
177 |
An example of this is an information holder with name/value pairs.
|
|
|
178 |
Say we have a configuration class like...
|
|
|
179 |
<pre>
|
|
|
180 |
class Configuration {
|
|
|
181 |
function Configuration() {
|
|
|
182 |
}
|
|
|
183 |
|
|
|
184 |
function getValue($key) {
|
|
|
185 |
}
|
|
|
186 |
}
|
|
|
187 |
</pre>
|
|
|
188 |
This is a classic situation for using stub objects as
|
|
|
189 |
actual configuration will vary from machine to machine,
|
|
|
190 |
hardly helping the reliability of our tests if we use it
|
|
|
191 |
directly.
|
|
|
192 |
The problem though is that all the data comes through the
|
|
|
193 |
<span class="new_code">getValue()</span> method and yet
|
|
|
194 |
we want different results for different keys.
|
|
|
195 |
Luckily the stubs have a filter system...
|
|
|
196 |
<pre>
|
|
|
197 |
<strong>Stub::generate('Configuration');
|
|
|
198 |
|
|
|
199 |
$config = &new StubConfiguration();
|
|
|
200 |
$config->setReturnValue('getValue', 'primary', array('db_host'));
|
|
|
201 |
$config->setReturnValue('getValue', 'admin', array('db_user'));
|
|
|
202 |
$config->setReturnValue('getValue', 'secret', array('db_password'));</strong>
|
|
|
203 |
</pre>
|
|
|
204 |
The extra parameter is a list of arguments to attempt
|
|
|
205 |
to match.
|
|
|
206 |
In this case we are trying to match only one argument which
|
|
|
207 |
is the look up key.
|
|
|
208 |
Now when the server stub has the
|
|
|
209 |
<span class="new_code">getValue()</span> method invoked
|
|
|
210 |
like this...
|
|
|
211 |
<pre>
|
|
|
212 |
$config->getValue('db_user');
|
|
|
213 |
</pre>
|
|
|
214 |
...it will return "admin".
|
|
|
215 |
It finds this by attempting to match the calling arguments
|
|
|
216 |
to its list of returns one after another until
|
|
|
217 |
a complete match is found.
|
|
|
218 |
</p>
|
|
|
219 |
<p>
|
|
|
220 |
You can set a default argument argument like so...
|
|
|
221 |
<pre>
|
|
|
222 |
<strong>
|
|
|
223 |
$config->setReturnValue('getValue', false, array('*'));</strong>
|
|
|
224 |
</pre>
|
|
|
225 |
This is not the same as setting the return value without
|
|
|
226 |
any argument requirements like this...
|
|
|
227 |
<pre>
|
|
|
228 |
<strong>
|
|
|
229 |
$config->setReturnValue('getValue', false);</strong>
|
|
|
230 |
</pre>
|
|
|
231 |
In the first case it will accept any single argument,
|
|
|
232 |
but exactly one is required.
|
|
|
233 |
In the second case any number of arguments will do and
|
|
|
234 |
it acts as a catchall after all other matches.
|
|
|
235 |
Note that if we add further single parameter options after
|
|
|
236 |
the wildcard in the first case, they will be ignored as the wildcard
|
|
|
237 |
will match first.
|
|
|
238 |
With complex parameter lists the ordering could be important
|
|
|
239 |
or else desired matches could be masked by earlier wildcard
|
|
|
240 |
ones.
|
|
|
241 |
Declare the most specific matches first if you are not sure.
|
|
|
242 |
</p>
|
|
|
243 |
<p>
|
|
|
244 |
There are times when you want a specific object to be
|
|
|
245 |
dished out by the stub rather than just a copy.
|
|
|
246 |
The PHP copy semantics force us to use a different method
|
|
|
247 |
for this.
|
|
|
248 |
You might be simulating a container for example...
|
|
|
249 |
<pre>
|
|
|
250 |
class Thing {
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
class Vector {
|
|
|
254 |
function Vector() {
|
|
|
255 |
}
|
|
|
256 |
|
|
|
257 |
function get($index) {
|
|
|
258 |
}
|
|
|
259 |
}
|
|
|
260 |
</pre>
|
|
|
261 |
In this case you can set a reference into the stub's
|
|
|
262 |
return list...
|
|
|
263 |
<pre>
|
|
|
264 |
Stub::generate('Vector');
|
|
|
265 |
|
|
|
266 |
$thing = new Thing();<strong>
|
|
|
267 |
$vector = &new StubVector();
|
|
|
268 |
$vector->setReturnReference('get', $thing, array(12));</strong>
|
|
|
269 |
</pre>
|
|
|
270 |
With this arrangement you know that every time
|
|
|
271 |
<span class="new_code">$vector->get(12)</span> is
|
|
|
272 |
called it will return the same
|
|
|
273 |
<span class="new_code">$thing</span> each time.
|
|
|
274 |
</p>
|
|
|
275 |
<p>
|
|
|
276 |
These three factors, timing, parameters and whether to copy,
|
|
|
277 |
can be combined orthogonally.
|
|
|
278 |
For example...
|
|
|
279 |
<pre>
|
|
|
280 |
$complex = &new StubComplexThing();
|
|
|
281 |
$stuff = new Stuff();<strong>
|
|
|
282 |
$complex->setReturnReferenceAt(3, 'get', $stuff, array('*', 1));</strong>
|
|
|
283 |
</pre>
|
|
|
284 |
This will return the <span class="new_code">$stuff</span> only on the third
|
|
|
285 |
call and only if two parameters were set the second of
|
|
|
286 |
which must be the integer 1.
|
|
|
287 |
That should cover most simple prototyping situations.
|
|
|
288 |
</p>
|
|
|
289 |
<p>
|
|
|
290 |
A final tricky case is one object creating another, known
|
|
|
291 |
as a factory pattern.
|
|
|
292 |
Suppose that on a successful query to our imaginary
|
|
|
293 |
database, a result set is returned as an iterator with
|
|
|
294 |
each call to <span class="new_code">next()</span> giving
|
|
|
295 |
one row until false.
|
|
|
296 |
This sounds like a simulation nightmare, but in fact it can all
|
|
|
297 |
be stubbed using the mechanics above.
|
|
|
298 |
</p>
|
|
|
299 |
<p>
|
|
|
300 |
Here's how...
|
|
|
301 |
<pre>
|
|
|
302 |
Stub::generate('DatabaseConnection');
|
|
|
303 |
Stub::generate('ResultIterator');
|
|
|
304 |
|
|
|
305 |
class DatabaseTest extends UnitTestCase {
|
|
|
306 |
|
|
|
307 |
function testUserFinder() {<strong>
|
|
|
308 |
$result = &new StubResultIterator();
|
|
|
309 |
$result->setReturnValue('next', false);
|
|
|
310 |
$result->setReturnValueAt(0, 'next', array(1, 'tom'));
|
|
|
311 |
$result->setReturnValueAt(1, 'next', array(3, 'dick'));
|
|
|
312 |
$result->setReturnValueAt(2, 'next', array(6, 'harry'));
|
|
|
313 |
|
|
|
314 |
$connection = &new StubDatabaseConnection();
|
|
|
315 |
$connection->setReturnValue('query', false);
|
|
|
316 |
$connection->setReturnReference(
|
|
|
317 |
'query',
|
|
|
318 |
$result,
|
|
|
319 |
array('select id, name from users'));</strong>
|
|
|
320 |
|
|
|
321 |
$finder = &new UserFinder($connection);
|
|
|
322 |
$this->assertIdentical(
|
|
|
323 |
$finder->findNames(),
|
|
|
324 |
array('tom', 'dick', 'harry'));
|
|
|
325 |
}
|
|
|
326 |
}
|
|
|
327 |
</pre>
|
|
|
328 |
Now only if our
|
|
|
329 |
<span class="new_code">$connection</span> is called with the correct
|
|
|
330 |
<span class="new_code">query()</span> will the
|
|
|
331 |
<span class="new_code">$result</span> be returned that is
|
|
|
332 |
itself exhausted after the third call to <span class="new_code">next()</span>.
|
|
|
333 |
This should be enough
|
|
|
334 |
information for our <span class="new_code">UserFinder</span> class,
|
|
|
335 |
the class actually
|
|
|
336 |
being tested here, to come up with goods.
|
|
|
337 |
A very precise test and not a real database in sight.
|
|
|
338 |
</p>
|
|
|
339 |
|
|
|
340 |
<p>
|
|
|
341 |
<a class="target" name="options">
|
|
|
342 |
<h2>Stub creation options</h2>
|
|
|
343 |
</a>
|
|
|
344 |
</p>
|
|
|
345 |
<p>
|
|
|
346 |
There are some additional options when creating stubs.
|
|
|
347 |
At the generation stage we can change the class name...
|
|
|
348 |
<pre>
|
|
|
349 |
<strong>Stub::generate('Iterator', 'MyStubIterator');
|
|
|
350 |
$iterator = &new MyStubIterator();
|
|
|
351 |
</strong>
|
|
|
352 |
</pre>
|
|
|
353 |
This is not very useful in itself as there would be no difference
|
|
|
354 |
in this class and the default except for the name.
|
|
|
355 |
However we can also add additional methods not found in the
|
|
|
356 |
original interface...
|
|
|
357 |
<pre>
|
|
|
358 |
class Iterator {
|
|
|
359 |
}
|
|
|
360 |
<strong>Stub::generate('Iterator', 'PrototypeIterator', array('next', 'isError'));
|
|
|
361 |
$iterator = &new PrototypeIterator();
|
|
|
362 |
$iterator->setReturnValue('next', 0);
|
|
|
363 |
</strong>
|
|
|
364 |
</pre>
|
|
|
365 |
The <span class="new_code">next()</span> and
|
|
|
366 |
<span class="new_code">isError()</span> methods can now have
|
|
|
367 |
return values set just as if they existed in the original class.
|
|
|
368 |
</p>
|
|
|
369 |
<p>
|
|
|
370 |
One other esoteric way of customising the stubs is to change
|
|
|
371 |
the default wildcard used for parameter matching.
|
|
|
372 |
<pre>
|
|
|
373 |
<strong>Stub::generate('Connection');
|
|
|
374 |
$iterator = &new StubConnection('wild');
|
|
|
375 |
$iterator->setReturnValue('query', array('id' => 33), array('wild'));
|
|
|
376 |
</strong>
|
|
|
377 |
</pre>
|
|
|
378 |
The only reason to do this is if you genuinely wanted to test
|
|
|
379 |
against the literal string "*" and didn't want it
|
|
|
380 |
interpreted as "any".
|
|
|
381 |
</p>
|
|
|
382 |
|
|
|
383 |
</div>
|
|
|
384 |
<div class="copyright">
|
|
|
385 |
Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
|
|
|
386 |
</div>
|
|
|
387 |
</body>
|
|
|
388 |
</html>
|