diff options
author | wei <> | 2006-07-05 07:40:57 +0000 |
---|---|---|
committer | wei <> | 2006-07-05 07:40:57 +0000 |
commit | dfa5aa5fbf11f89ce483c58016465ddc3921f082 (patch) | |
tree | f01dd1c13d700b266695e503b3ebb6e05e591410 /tests/test_tools/simpletest/docs/en/server_stubs_documentation.html | |
parent | b6dfb6c447cf502e694d299dbda1b2e092c3312d (diff) |
move to tests
Diffstat (limited to 'tests/test_tools/simpletest/docs/en/server_stubs_documentation.html')
-rwxr-xr-x | tests/test_tools/simpletest/docs/en/server_stubs_documentation.html | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/tests/test_tools/simpletest/docs/en/server_stubs_documentation.html b/tests/test_tools/simpletest/docs/en/server_stubs_documentation.html new file mode 100755 index 00000000..4b18bb0b --- /dev/null +++ b/tests/test_tools/simpletest/docs/en/server_stubs_documentation.html @@ -0,0 +1,388 @@ +<html> +<head> +<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>SimpleTest for PHP server stubs documentation</title> +<link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> +</head> +<body> +<div class="menu_back"> +<div class="menu"> +<h2> +<a href="index.html">SimpleTest</a> +</h2> +<ul> +<li> +<a href="overview.html">Overview</a> +</li> +<li> +<a href="unit_test_documentation.html">Unit tester</a> +</li> +<li> +<a href="group_test_documentation.html">Group tests</a> +</li> +<li> +<span class="chosen">Server stubs</span> +</li> +<li> +<a href="mock_objects_documentation.html">Mock objects</a> +</li> +<li> +<a href="partial_mocks_documentation.html">Partial mocks</a> +</li> +<li> +<a href="reporter_documentation.html">Reporting</a> +</li> +<li> +<a href="expectation_documentation.html">Expectations</a> +</li> +<li> +<a href="web_tester_documentation.html">Web tester</a> +</li> +<li> +<a href="form_testing_documentation.html">Testing forms</a> +</li> +<li> +<a href="authentication_documentation.html">Authentication</a> +</li> +<li> +<a href="browser_documentation.html">Scriptable browser</a> +</li> +</ul> +</div> +</div> +<h1>Server stubs documentation</h1> +<div class="content"> + <p> +<a class="target" name="what"> +<h2>What are server stubs?</h2> +</a> +</p> + <p> + This was originally a pattern named by Robert Binder (Testing + object-oriented systems: models, patterns, and tools, + Addison-Wesley) in 1999. + A server stub is a simulation of an object or component. + It should exactly replace a component in a system for test + or prototyping purposes, but remain lightweight. + This allows tests to run more quickly, or if the simulated + class has not been written, to run at all. + </p> + + <p> +<a class="target" name="creation"> +<h2>Creating server stubs</h2> +</a> +</p> + <p> + All we need is an existing class, say a database connection + that looks like this... +<pre> +<strong>class DatabaseConnection { + function DatabaseConnection() { + } + + function query() { + } + + function selectQuery() { + } +}</strong> +</pre> + The class does not need to have been implemented yet. + To create a stub version of the class we need to include the + server stub library and run the generator... +<pre> +<strong>require_once('simpletest/mock_objects.php'); +require_once('database_connection.php'); +Stub::generate('DatabaseConnection');</strong> +</pre> + This generates a clone class called + <span class="new_code">StubDatabaseConnection</span>. + We can now create instances of the new class within + our prototype script... +<pre> +require_once('simpletest/mock_objects.php'); +require_once('database_connection.php'); +Stub::generate('DatabaseConnection'); +<strong> +$connection = new StubDatabaseConnection(); +</strong> +</pre> + The stub version of a class has all the methods of the original + so that operations like + <span class="new_code">$connection->query()</span> are still + legal. + The return value will be <span class="new_code">null</span>, + but we can change that with... +<pre> +<strong>$connection->setReturnValue('query', 37)</strong> +</pre> + Now every time we call + <span class="new_code">$connection->query()</span> we get + the result of 37. + We can set the return value to anything, say a hash of + imaginary database results or a list of persistent objects. + Parameters are irrelevant here, we always get the same + values back each time once they have been set up this way. + That may not sound like a convincing replica of a + database connection, but for the half a dozen lines of + a test method it is usually all you need. + </p> + + <p> +<a class="target" name="patterns"> +<h2>Simulation patterns</h2> +</a> +</p> + <p> + Things aren't always that simple though. + One common problem is iterators, where constantly returning + the same value could cause an endless loop in the object + being tested. + For these we need to set up sequences of values. + Let's say we have a simple iterator that looks like this... +<pre> +class Iterator { + function Iterator() { + } + + function next() { + } +} +</pre> + This is about the simplest iterator you could have. + Assuming that this iterator only returns text until it + reaches the end, when it returns false, we can simulate it + with... +<pre> +<strong>Stub::generate('Iterator'); + +$iterator = new StubIterator(); +$iterator->setReturnValue('next', false); +$iterator->setReturnValueAt(0, 'next', 'First string'); +$iterator->setReturnValueAt(1, 'next', 'Second string');</strong> +</pre> + When <span class="new_code">next()</span> is called on the + stub iterator it will first return "First string", + on the second call "Second string" will be returned + and on any other call <span class="new_code">false</span> will + be returned. + The sequenced return values take precedence over the constant + return value. + The constant one is a kind of default if you like. + </p> + <p> + Another tricky situation is an overloaded + <span class="new_code">get()</span> operation. + An example of this is an information holder with name/value pairs. + Say we have a configuration class like... +<pre> +class Configuration { + function Configuration() { + } + + function getValue($key) { + } +} +</pre> + This is a classic situation for using stub objects as + actual configuration will vary from machine to machine, + hardly helping the reliability of our tests if we use it + directly. + The problem though is that all the data comes through the + <span class="new_code">getValue()</span> method and yet + we want different results for different keys. + Luckily the stubs have a filter system... +<pre> +<strong>Stub::generate('Configuration'); + +$config = &new StubConfiguration(); +$config->setReturnValue('getValue', 'primary', array('db_host')); +$config->setReturnValue('getValue', 'admin', array('db_user')); +$config->setReturnValue('getValue', 'secret', array('db_password'));</strong> +</pre> + The extra parameter is a list of arguments to attempt + to match. + In this case we are trying to match only one argument which + is the look up key. + Now when the server stub has the + <span class="new_code">getValue()</span> method invoked + like this... +<pre> +$config->getValue('db_user'); +</pre> + ...it will return "admin". + It finds this by attempting to match the calling arguments + to its list of returns one after another until + a complete match is found. + </p> + <p> + You can set a default argument argument like so... +<pre> +<strong> +$config->setReturnValue('getValue', false, array('*'));</strong> +</pre> + This is not the same as setting the return value without + any argument requirements like this... +<pre> +<strong> +$config->setReturnValue('getValue', false);</strong> +</pre> + In the first case it will accept any single argument, + but exactly one is required. + In the second case any number of arguments will do and + it acts as a catchall after all other matches. + Note that if we add further single parameter options after + the wildcard in the first case, they will be ignored as the wildcard + will match first. + With complex parameter lists the ordering could be important + or else desired matches could be masked by earlier wildcard + ones. + Declare the most specific matches first if you are not sure. + </p> + <p> + There are times when you want a specific object to be + dished out by the stub rather than just a copy. + The PHP copy semantics force us to use a different method + for this. + You might be simulating a container for example... +<pre> +class Thing { +} + +class Vector { + function Vector() { + } + + function get($index) { + } +} +</pre> + In this case you can set a reference into the stub's + return list... +<pre> +Stub::generate('Vector'); + +$thing = new Thing();<strong> +$vector = &new StubVector(); +$vector->setReturnReference('get', $thing, array(12));</strong> +</pre> + With this arrangement you know that every time + <span class="new_code">$vector->get(12)</span> is + called it will return the same + <span class="new_code">$thing</span> each time. + </p> + <p> + These three factors, timing, parameters and whether to copy, + can be combined orthogonally. + For example... +<pre> +$complex = &new StubComplexThing(); +$stuff = new Stuff();<strong> +$complex->setReturnReferenceAt(3, 'get', $stuff, array('*', 1));</strong> +</pre> + This will return the <span class="new_code">$stuff</span> only on the third + call and only if two parameters were set the second of + which must be the integer 1. + That should cover most simple prototyping situations. + </p> + <p> + A final tricky case is one object creating another, known + as a factory pattern. + Suppose that on a successful query to our imaginary + database, a result set is returned as an iterator with + each call to <span class="new_code">next()</span> giving + one row until false. + This sounds like a simulation nightmare, but in fact it can all + be stubbed using the mechanics above. + </p> + <p> + Here's how... +<pre> +Stub::generate('DatabaseConnection'); +Stub::generate('ResultIterator'); + +class DatabaseTest extends UnitTestCase { + + function testUserFinder() {<strong> + $result = &new StubResultIterator(); + $result->setReturnValue('next', false); + $result->setReturnValueAt(0, 'next', array(1, 'tom')); + $result->setReturnValueAt(1, 'next', array(3, 'dick')); + $result->setReturnValueAt(2, 'next', array(6, 'harry')); + + $connection = &new StubDatabaseConnection(); + $connection->setReturnValue('query', false); + $connection->setReturnReference( + 'query', + $result, + array('select id, name from users'));</strong> + + $finder = &new UserFinder($connection); + $this->assertIdentical( + $finder->findNames(), + array('tom', 'dick', 'harry')); + } +} +</pre> + Now only if our + <span class="new_code">$connection</span> is called with the correct + <span class="new_code">query()</span> will the + <span class="new_code">$result</span> be returned that is + itself exhausted after the third call to <span class="new_code">next()</span>. + This should be enough + information for our <span class="new_code">UserFinder</span> class, + the class actually + being tested here, to come up with goods. + A very precise test and not a real database in sight. + </p> + + <p> +<a class="target" name="options"> +<h2>Stub creation options</h2> +</a> +</p> + <p> + There are some additional options when creating stubs. + At the generation stage we can change the class name... +<pre> +<strong>Stub::generate('Iterator', 'MyStubIterator'); +$iterator = &new MyStubIterator(); +</strong> +</pre> + This is not very useful in itself as there would be no difference + in this class and the default except for the name. + However we can also add additional methods not found in the + original interface... +<pre> +class Iterator { +} +<strong>Stub::generate('Iterator', 'PrototypeIterator', array('next', 'isError')); +$iterator = &new PrototypeIterator(); +$iterator->setReturnValue('next', 0); +</strong> +</pre> + The <span class="new_code">next()</span> and + <span class="new_code">isError()</span> methods can now have + return values set just as if they existed in the original class. + </p> + <p> + One other esoteric way of customising the stubs is to change + the default wildcard used for parameter matching. +<pre> +<strong>Stub::generate('Connection'); +$iterator = &new StubConnection('wild'); +$iterator->setReturnValue('query', array('id' => 33), array('wild')); +</strong> +</pre> + The only reason to do this is if you genuinely wanted to test + against the literal string "*" and didn't want it + interpreted as "any". + </p> + + </div> +<div class="copyright"> + Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 + </div> +</body> +</html> |