diff options
author | Fabio Bas <ctrlaltca@gmail.com> | 2016-03-25 17:55:51 +0100 |
---|---|---|
committer | Fabio Bas <ctrlaltca@gmail.com> | 2016-03-25 17:55:51 +0100 |
commit | a3388622287e218beddfa14a47ed677d4307b36b (patch) | |
tree | 1b4c7ac8597b1cc798b6683d4a81c90d38de12f6 /tests/test_tools/simpletest/docs/en/mock_objects_documentation.html | |
parent | c7fd3e1167b6f2fa7746edbd0fb8f8c1694c61f9 (diff) |
Removed simpletest and moved all tests in the unit tree
Tests are executed now, but a lot of them need fixing.
Diffstat (limited to 'tests/test_tools/simpletest/docs/en/mock_objects_documentation.html')
-rwxr-xr-x | tests/test_tools/simpletest/docs/en/mock_objects_documentation.html | 727 |
1 files changed, 0 insertions, 727 deletions
diff --git a/tests/test_tools/simpletest/docs/en/mock_objects_documentation.html b/tests/test_tools/simpletest/docs/en/mock_objects_documentation.html deleted file mode 100755 index eb32c619..00000000 --- a/tests/test_tools/simpletest/docs/en/mock_objects_documentation.html +++ /dev/null @@ -1,727 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest for PHP mock objects 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">Mock objects</span> -</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>Mock objects documentation</h1> -<div class="content"> - <p> -<a class="target" name="what"> -<h2>What are mock objects?</h2> -</a> -</p> - <p> - Mock objects have two roles during a test case: actor and critic. - </p> - <p> - The actor behaviour is to simulate objects that are difficult to - set up or time consuming to set up for a test. - The classic example is a database connection. - Setting up a test database at the start of each test would slow - testing to a crawl and would require the installation of the - database engine and test data on the test machine. - If we can simulate the connection and return data of our - choosing we not only win on the pragmatics of testing, but can - also feed our code spurious data to see how it responds. - We can simulate databases being down or other extremes - without having to create a broken database for real. - In other words, we get greater control of the test environment. - </p> - <p> - If mock objects only behaved as actors they would simply be - known as server stubs. - This was originally a pattern named by Robert Binder (Testing - object-oriented systems: models, patterns, and tools, - Addison-Wesley) in 1999. - </p> - <p> - 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> - However, the mock objects not only play a part (by supplying chosen - return values on demand) they are also sensitive to the - messages sent to them (via expectations). - By setting expected parameters for a method call they act - as a guard that the calls upon them are made correctly. - If expectations are not met they save us the effort of - writing a failed test assertion by performing that duty on our - behalf. - </p> - <p> - In the case of an imaginary database connection they can - test that the query, say SQL, was correctly formed by - the object that is using the connection. - Set them up with fairly tight expectations and you will - hardly need manual assertions at all. - </p> - - <p> -<a class="target" name="creation"> -<h2>Creating mock objects</h2> -</a> -</p> - <p> - In the same way that we create server stubs, 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 mock version of the class we need to include the - mock object library and run the generator... -<pre> -<strong>require_once('simpletest/unit_tester.php'); -require_once('simpletest/mock_objects.php'); -require_once('database_connection.php'); - -Mock::generate('DatabaseConnection');</strong> -</pre> - This generates a clone class called - <span class="new_code">MockDatabaseConnection</span>. - We can now create instances of the new class within - our test case... -<pre> -require_once('simpletest/unit_tester.php'); -require_once('simpletest/mock_objects.php'); -require_once('database_connection.php'); - -Mock::generate('DatabaseConnection'); -<strong> -class MyTestCase extends UnitTestCase { - - function testSomething() { - $connection = &new MockDatabaseConnection(); - } -}</strong> -</pre> - Unlike the generated stubs the mock constructor needs a reference - to the test case so that it can dispatch passes and failures while - checking its expectations. - This means that mock objects can only be used within test cases. - Despite this their extra power means that stubs are hardly ever used - if mocks are available. - </p> - <p> - <a class="target" name="stub"> -<h2>Mocks as actors</h2> -</a> - </p> - <p> - The mock 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> - We can also add extra methods to the mock when generating it - and choose our own class name... -<pre> -<strong>Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));</strong> -</pre> - Here the mock will behave as if the <span class="new_code">setOptions()</span> - existed in the original class. - This is handy if a class has used the PHP <span class="new_code">overload()</span> - mechanism to add dynamic methods. - You can create a special mock to simulate this situation. - </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> -Mock::generate('Iterator'); - -class IteratorTest extends UnitTestCase() { - - function testASequence() {<strong> - $iterator = &new MockIterator(); - $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 - mock 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 mock 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 mocks have a filter system... -<pre> -<strong>$config = &new MockConfiguration(); -$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 mock object 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 mock rather than a copy. - The PHP4 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 mock's - return list... -<pre> -$thing = &new Thing();<strong> -$vector = &new MockVector(); -$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. - This is compatible with PHP5 as well. - </p> - <p> - These three factors, timing, parameters and whether to copy, - can be combined orthogonally. - For example... -<pre> -$complex = &new MockComplexThing(); -$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 mocked using the mechanics above. - </p> - <p> - Here's how... -<pre> -Mock::generate('DatabaseConnection'); -Mock::generate('ResultIterator'); - -class DatabaseTest extends UnitTestCase { - - function testUserFinder() {<strong> - $result = &new MockResultIterator(); - $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 MockDatabaseConnection(); - $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="expectations"> -<h2>Mocks as critics</h2> -</a> -</p> - <p> - Although the server stubs approach insulates your tests from - real world disruption, it is only half the benefit. - You can have the class under test receiving the required - messages, but is your new class sending correct ones? - Testing this can get messy without a mock objects library. - </p> - <p> - By way of example, suppose we have a - <span class="new_code">SessionPool</span> class that we - want to add logging to. - Rather than grow the original class into something more - complicated, we want to add this behaviour with a decorator (GOF). - The <span class="new_code">SessionPool</span> code currently looks - like this... -<pre> -<strong>class SessionPool { - function SessionPool() { - ... - } - - function &findSession($cookie) { - ... - } - ... -} - -class Session { - ... -}</strong> -</php> - While our logging code looks like this... -<php><strong> -class Log { - function Log() { - ... - } - - function message() { - ... - } -} - -class LoggingSessionPool { - function LoggingSessionPool(&$session_pool, &$log) { - ... - } - - function &findSession(\$cookie) { - ... - } - ... -}</strong> -</pre> - Out of all of this, the only class we want to test here - is the <span class="new_code">LoggingSessionPool</span>. - In particular we would like to check that the - <span class="new_code">findSession()</span> method is - called with the correct session ID in the cookie and that - it sent the message "Starting session $cookie" - to the logger. - </p> - <p> - Despite the fact that we are testing only a few lines of - production code, here is what we would have to do in a - conventional test case: - <ol> - <li>Create a log object.</li> - <li>Set a directory to place the log file.</li> - <li>Set the directory permissions so we can write the log.</li> - <li>Create a <span class="new_code">SessionPool</span> object.</li> - <li>Hand start a session, which probably does lot's of things.</li> - <li>Invoke <span class="new_code">findSession()</span>.</li> - <li>Read the new Session ID (hope there is an accessor!).</li> - <li>Raise a test assertion to confirm that the ID matches the cookie.</li> - <li>Read the last line of the log file.</li> - <li>Pattern match out the extra logging timestamps, etc.</li> - <li>Assert that the session message is contained in the text.</li> - </ol> - It is hardly surprising that developers hate writing tests - when they are this much drudgery. - To make things worse, every time the logging format changes or - the method of creating new sessions changes, we have to rewrite - parts of this test even though this test does not officially - test those parts of the system. - We are creating headaches for the writers of these other classes. - </p> - <p> - Instead, here is the complete test method using mock object magic... -<pre> -Mock::generate('Session'); -Mock::generate('SessionPool'); -Mock::generate('Log'); - -class LoggingSessionPoolTest extends UnitTestCase { - ... - function testFindSessionLogging() {<strong> - $session = &new MockSession(); - $pool = &new MockSessionPool(); - $pool->setReturnReference('findSession', $session); - $pool->expectOnce('findSession', array('abc')); - - $log = &new MockLog(); - $log->expectOnce('message', array('Starting session abc')); - - $logging_pool = &new LoggingSessionPool($pool, $log); - $this->assertReference($logging_pool->findSession('abc'), $session);</strong> - } -} -</pre> - We start by creating a dummy session. - We don't have to be too fussy about this as the check - for which session we want is done elsewhere. - We only need to check that it was the same one that came - from the session pool. - </p> - <p> - <span class="new_code">findSession()</span> is a factory - method the simulation of which is described <a href="#stub">above</a>. - The point of departure comes with the first - <span class="new_code">expectOnce()</span> call. - This line states that whenever - <span class="new_code">findSession()</span> is invoked on the - mock, it will test the incoming arguments. - If it receives the single argument of a string "abc" - then a test pass is sent to the unit tester, otherwise a fail is - generated. - This was the part where we checked that the right session was asked for. - The argument list follows the same format as the one for setting - return values. - You can have wildcards and sequences and the order of - evaluation is the same. - </p> - <p> - We use the same pattern to set up the mock logger. - We tell it that it should have - <span class="new_code">message()</span> invoked - once only with the argument "Starting session abc". - By testing the calling arguments, rather than the logger output, - we insulate the test from any display changes in the logger. - </p> - <p> - We start to run our tests when we create the new - <span class="new_code">LoggingSessionPool</span> and feed - it our preset mock objects. - Everything is now under our control. - </p> - <p> - This is still quite a bit of test code, but the code is very - strict. - If it still seems rather daunting there is a lot less of it - than if we tried this without mocks and this particular test, - interactions rather than output, is always more work to set - up. - More often you will be testing more complex situations without - needing this level or precision. - Also some of this can be refactored into a test case - <span class="new_code">setUp()</span> method. - </p> - <p> - Here is the full list of expectations you can set on a mock object - in <a href="http://www.lastcraft.com/simple_test.php">SimpleTest</a>... - <table> -<thead> - <tr> -<th>Expectation</th><th>Needs <span class="new_code">tally()</span></th> -</tr> - </thead> -<tbody> -<tr> - <td><span class="new_code">expect($method, $args)</span></td> - <td style="text-align: center">No</td> - </tr> - <tr> - <td><span class="new_code">expectAt($timing, $method, $args)</span></td> - <td style="text-align: center">No</td> - </tr> - <tr> - <td><span class="new_code">expectCallCount($method, $count)</span></td> - <td style="text-align: center">Yes</td> - </tr> - <tr> - <td><span class="new_code">expectMaximumCallCount($method, $count)</span></td> - <td style="text-align: center">No</td> - </tr> - <tr> - <td><span class="new_code">expectMinimumCallCount($method, $count)</span></td> - <td style="text-align: center">Yes</td> - </tr> - <tr> - <td><span class="new_code">expectNever($method)</span></td> - <td style="text-align: center">No</td> - </tr> - <tr> - <td><span class="new_code">expectOnce($method, $args)</span></td> - <td style="text-align: center">Yes</td> - </tr> - <tr> - <td><span class="new_code">expectAtLeastOnce($method, $args)</span></td> - <td style="text-align: center">Yes</td> - </tr> - </tbody> -</table> - Where the parameters are... - <dl> - <dt class="new_code">$method</dt> - <dd>The method name, as a string, to apply the condition to.</dd> - <dt class="new_code">$args</dt> - <dd> - The arguments as a list. Wildcards can be included in the same - manner as for <span class="new_code">setReturn()</span>. - This argument is optional for <span class="new_code">expectOnce()</span> - and <span class="new_code">expectAtLeastOnce()</span>. - </dd> - <dt class="new_code">$timing</dt> - <dd> - The only point in time to test the condition. - The first call starts at zero. - </dd> - <dt class="new_code">$count</dt> - <dd>The number of calls expected.</dd> - </dl> - The method <span class="new_code">expectMaximumCallCount()</span> - is slightly different in that it will only ever generate a failure. - It is silent if the limit is never reached. - </p> - <p> - Like the assertions within test cases, all of the expectations - can take a message override as an extra parameter. - Also the original failure message can be embedded in the output - as "%s". - </p> - - <p> -<a class="target" name="approaches"> -<h2>Other approaches</h2> -</a> -</p> - <p> - There are three approaches to creating mocks including the one - that SimpleTest employs. - Coding them by hand using a base class, generating them to - a file and dynamically generating them on the fly. - </p> - <p> - Mock objects generated with <a href="simple_test.html">SimpleTest</a> - are dynamic. - They are created at run time in memory, using - <span class="new_code">eval()</span>, rather than written - out to a file. - This makes the mocks easy to create, a one liner, - especially compared with hand - crafting them in a parallel class hierarchy. - The problem is that the behaviour is usually set up in the tests - themselves. - If the original objects change the mock versions - that the tests rely on can get out of sync. - This can happen with the parallel hierarchy approach as well, - but is far more quickly detected. - </p> - <p> - The solution, of course, is to add some real integration - tests. - You don't need very many and the convenience gained - from the mocks more than outweighs the small amount of - extra testing. - You cannot trust code that was only tested with mocks. - </p> - <p> - If you are still determined to build static libraries of mocks - because you want to simulate very specific behaviour, you can - achieve the same effect using the SimpleTest class generator. - In your library file, say <em>mocks/connection.php</em> for a - database connection, create a mock and inherit to override - special methods or add presets... -<pre> -<?php - require_once('simpletest/mock_objects.php'); - require_once('../classes/connection.php'); -<strong> - Mock::generate('Connection', 'BasicMockConnection'); - class MockConnection extends BasicMockConnection { - function MockConnection() { - $this->BasicMockConnection(); - $this->setReturn('query', false); - } - }</strong> -?> -</pre> - The generate call tells the class generator to create - a class called <span class="new_code">BasicMockConnection</span> - rather than the usual <span class="new_code">MockConnection</span>. - We then inherit from this to get our version of - <span class="new_code">MockConnection</span>. - By intercepting in this way we can add behaviour, here setting - the default value of <span class="new_code">query()</span> to be false. - By using the default name we make sure that the mock class - generator will not recreate a different one when invoked elsewhere in the - tests. - It never creates a class if it already exists. - As long as the above file is included first then all tests - that generated <span class="new_code">MockConnection</span> should - now be using our one instead. - If we don't get the order right and the mock library - creates one first then the class creation will simply fail. - </p> - <p> - Use this trick if you find you have a lot of common mock behaviour - or you are getting frequent integration problems at later - stages of testing. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> |