summaryrefslogtreecommitdiff
path: root/tests/test_tools/simpletest/docs/en/mock_objects_documentation.html
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_tools/simpletest/docs/en/mock_objects_documentation.html')
-rwxr-xr-xtests/test_tools/simpletest/docs/en/mock_objects_documentation.html727
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 = &amp;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-&gt;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-&gt;setReturnValue('query', 37)</strong>
-</pre>
- Now every time we call
- <span class="new_code">$connection-&gt;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 = &amp;new MockIterator();
- $iterator-&gt;setReturnValue('next', false);
- $iterator-&gt;setReturnValueAt(0, 'next', 'First string');
- $iterator-&gt;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 = &amp;new MockConfiguration();
-$config-&gt;setReturnValue('getValue', 'primary', array('db_host'));
-$config-&gt;setReturnValue('getValue', 'admin', array('db_user'));
-$config-&gt;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-&gt;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-&gt;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-&gt;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 = &amp;new Thing();<strong>
-$vector = &amp;new MockVector();
-$vector-&gt;setReturnReference('get', $thing, array(12));</strong>
-</pre>
- With this arrangement you know that every time
- <span class="new_code">$vector-&gt;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 = &amp;new MockComplexThing();
-$stuff = &amp;new Stuff();<strong>
-$complex-&gt;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 = &amp;new MockResultIterator();
- $result-&gt;setReturnValue('next', false);
- $result-&gt;setReturnValueAt(0, 'next', array(1, 'tom'));
- $result-&gt;setReturnValueAt(1, 'next', array(3, 'dick'));
- $result-&gt;setReturnValueAt(2, 'next', array(6, 'harry'));
-
- $connection = &amp;new MockDatabaseConnection();
- $connection-&gt;setReturnValue('query', false);
- $connection-&gt;setReturnReference(
- 'query',
- $result,
- array('select id, name from users'));</strong>
-
- $finder = &amp;new UserFinder($connection);
- $this-&gt;assertIdentical(
- $finder-&gt;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 &amp;findSession($cookie) {
- ...
- }
- ...
-}
-
-class Session {
- ...
-}</strong>
-&lt;/php&gt;
- While our logging code looks like this...
-&lt;php&gt;<strong>
-class Log {
- function Log() {
- ...
- }
-
- function message() {
- ...
- }
-}
-
-class LoggingSessionPool {
- function LoggingSessionPool(&amp;$session_pool, &amp;$log) {
- ...
- }
-
- function &amp;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 = &amp;new MockSession();
- $pool = &amp;new MockSessionPool();
- $pool-&gt;setReturnReference('findSession', $session);
- $pool-&gt;expectOnce('findSession', array('abc'));
-
- $log = &amp;new MockLog();
- $log-&gt;expectOnce('message', array('Starting session abc'));
-
- $logging_pool = &amp;new LoggingSessionPool($pool, $log);
- $this-&gt;assertReference($logging_pool-&gt;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>
-&lt;?php
- require_once('simpletest/mock_objects.php');
- require_once('../classes/connection.php');
-<strong>
- Mock::generate('Connection', 'BasicMockConnection');
- class MockConnection extends BasicMockConnection {
- function MockConnection() {
- $this-&gt;BasicMockConnection();
- $this-&gt;setReturn('query', false);
- }
- }</strong>
-?&gt;
-</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>