758 lines
32 KiB
HTML
758 lines
32 KiB
HTML
|
<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">
|
||
|
<a href="index.html">SimpleTest</a>
|
||
|
|
|
||
|
<a href="overview.html">Overview</a>
|
||
|
|
|
||
|
<a href="unit_test_documentation.html">Unit tester</a>
|
||
|
|
|
||
|
<a href="group_test_documentation.html">Group tests</a>
|
||
|
|
|
||
|
<span class="chosen">Mock objects</span>
|
||
|
|
|
||
|
<a href="partial_mocks_documentation.html">Partial mocks</a>
|
||
|
|
|
||
|
<a href="reporter_documentation.html">Reporting</a>
|
||
|
|
|
||
|
<a href="expectation_documentation.html">Expectations</a>
|
||
|
|
|
||
|
<a href="web_tester_documentation.html">Web tester</a>
|
||
|
|
|
||
|
<a href="form_testing_documentation.html">Testing forms</a>
|
||
|
|
|
||
|
<a href="authentication_documentation.html">Authentication</a>
|
||
|
|
|
||
|
<a href="browser_documentation.html">Scriptable browser</a>
|
||
|
</div></div>
|
||
|
<h1>Mock objects documentation</h1>
|
||
|
This page...
|
||
|
<ul>
|
||
|
<li>
|
||
|
<a href="#what">What are mock objects?</a>
|
||
|
</li>
|
||
|
<li>
|
||
|
<a href="#creation">Creating mock objects</a>.
|
||
|
</li>
|
||
|
<li>
|
||
|
<a href="#stub">Mocks as actors</a> or stubs.
|
||
|
</li>
|
||
|
<li>
|
||
|
<a href="#expectations">Mocks as critics</a> with expectations.
|
||
|
</li>
|
||
|
<li>
|
||
|
<a href="#approaches">Other approaches</a> including mock libraries.
|
||
|
</li>
|
||
|
</ul>
|
||
|
<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>
|
||
|
</pre>
|
||
|
While our logging code looks like this...
|
||
|
<pre>
|
||
|
<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>
|
||
|
Also if you have juste one call in your test, make sure you're using
|
||
|
<span class="new_code">expectOnce</span>.<br>
|
||
|
Using <span class="new_code">$mocked->expectAt(0, 'method', 'args);</span>
|
||
|
on its own will not be catched :
|
||
|
checking the arguments and the overall call count
|
||
|
are currently independant.
|
||
|
</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>
|
||
|
References and related information...
|
||
|
<ul>
|
||
|
<li>
|
||
|
The original
|
||
|
<a href="http://www.mockobjects.com/">Mock objects</a> paper.
|
||
|
</li>
|
||
|
<li>
|
||
|
SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>.
|
||
|
</li>
|
||
|
<li>
|
||
|
SimpleTest home page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>.
|
||
|
</li>
|
||
|
</ul>
|
||
|
<div class="menu_back"><div class="menu">
|
||
|
<a href="index.html">SimpleTest</a>
|
||
|
|
|
||
|
<a href="overview.html">Overview</a>
|
||
|
|
|
||
|
<a href="unit_test_documentation.html">Unit tester</a>
|
||
|
|
|
||
|
<a href="group_test_documentation.html">Group tests</a>
|
||
|
|
|
||
|
<span class="chosen">Mock objects</span>
|
||
|
|
|
||
|
<a href="partial_mocks_documentation.html">Partial mocks</a>
|
||
|
|
|
||
|
<a href="reporter_documentation.html">Reporting</a>
|
||
|
|
|
||
|
<a href="expectation_documentation.html">Expectations</a>
|
||
|
|
|
||
|
<a href="web_tester_documentation.html">Web tester</a>
|
||
|
|
|
||
|
<a href="form_testing_documentation.html">Testing forms</a>
|
||
|
|
|
||
|
<a href="authentication_documentation.html">Authentication</a>
|
||
|
|
|
||
|
<a href="browser_documentation.html">Scriptable browser</a>
|
||
|
</div></div>
|
||
|
<div class="copyright">
|
||
|
Copyright<br>Marcus Baker 2006
|
||
|
</div>
|
||
|
</body>
|
||
|
</html>
|