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 /test_tools/simpletest/docs/en/partial_mocks_documentation.html | |
parent | b6dfb6c447cf502e694d299dbda1b2e092c3312d (diff) |
move to tests
Diffstat (limited to 'test_tools/simpletest/docs/en/partial_mocks_documentation.html')
-rwxr-xr-x | test_tools/simpletest/docs/en/partial_mocks_documentation.html | 426 |
1 files changed, 0 insertions, 426 deletions
diff --git a/test_tools/simpletest/docs/en/partial_mocks_documentation.html b/test_tools/simpletest/docs/en/partial_mocks_documentation.html deleted file mode 100755 index 20749415..00000000 --- a/test_tools/simpletest/docs/en/partial_mocks_documentation.html +++ /dev/null @@ -1,426 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest for PHP partial mocks 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> -<a href="server_stubs_documentation.html">Server stubs</a> -</li> -<li> -<a href="mock_objects_documentation.html">Mock objects</a> -</li> -<li> -<span class="chosen">Partial mocks</span> -</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>Partial mock objects documentation</h1> -<div class="content"> - - <p> - A partial mock is simply a pattern to alleviate a specific problem - in testing with mock objects, - that of getting mock objects into tight corners. - It's quite a limited tool and possibly not even a good idea. - It is included with SimpleTest because I have found it useful - on more than one occasion and has saved a lot of work at that point. - </p> - - <p> -<a class="target" name="inject"> -<h2>The mock injection problem</h2> -</a> -</p> - <p> - When one object uses another it is very simple to just pass a mock - version in already set up with its expectations. - Things are rather tricker if one object creates another and the - creator is the one you want to test. - This means that the created object should be mocked, but we can - hardly tell our class under test to create a mock instead. - The tested class doesn't even know it is running inside a test - after all. - </p> - <p> - For example, suppose we are building a telnet client and it - needs to create a network socket to pass its messages. - The connection method might look something like... -<pre> -<strong><?php - require_once('socket.php'); - - class Telnet { - ... - function &connect($ip, $port, $username, $password) { - $socket = &new Socket($ip, $port); - $socket->read( ... ); - ... - } - } -?></strong> -</pre> - We would really like to have a mock object version of the socket - here, what can we do? - </p> - <p> - The first solution is to pass the socket in as a parameter, - forcing the creation up a level. - Having the client handle this is actually a very good approach - if you can manage it and should lead to factoring the creation from - the doing. - In fact, this is one way in which testing with mock objects actually - forces you to code more tightly focused solutions. - They improve your programming. - </p> - <p> - Here this would be... -<pre> -<?php - require_once('socket.php'); - - class Telnet { - ... - <strong>function &connect(&$socket, $username, $password) { - $socket->read( ... ); - ... - }</strong> - } -?> -</pre> - This means that the test code is typical for a test involving - mock objects. -<pre> -class TelnetTest extends UnitTestCase { - ... - function testConnection() {<strong> - $socket = &new MockSocket($this); - ... - $telnet = &new Telnet(); - $telnet->connect($socket, 'Me', 'Secret'); - ...</strong> - } -} -</pre> - It is pretty obvious though that one level is all you can go. - You would hardly want your top level application creating - every low level file, socket and database connection ever - needed. - It wouldn't know the constructor parameters anyway. - </p> - <p> - The next simplest compromise is to have the created object passed - in as an optional parameter... -<pre> -<?php - require_once('socket.php'); - - class Telnet { - ...<strong> - function &connect($ip, $port, $username, $password, $socket = false) { - if (!$socket) { - $socket = &new Socket($ip, $port); - } - $socket->read( ... );</strong> - ... - return $socket; - } - } -?> -</pre> - For a quick solution this is usually good enough. - The test now looks almost the same as if the parameter - was formally passed... -<pre> -class TelnetTest extends UnitTestCase { - ... - function testConnection() {<strong> - $socket = &new MockSocket($this); - ... - $telnet = &new Telnet(); - $telnet->connect('127.0.0.1', 21, 'Me', 'Secret', &$socket); - ...</strong> - } -} -</pre> - The problem with this approach is its untidiness. - There is test code in the main class and parameters passed - in the test case that are never used. - This is a quick and dirty approach, but nevertheless effective - in most situations. - </p> - <p> - The next method is to pass in a factory object to do the creation... -<pre> -<?php - require_once('socket.php'); - - class Telnet {<strong> - function Telnet(&$network) { - $this->_network = &$network; - }</strong> - ... - function &connect($ip, $port, $username, $password) {<strong> - $socket = &$this->_network->createSocket($ip, $port); - $socket->read( ... );</strong> - ... - return $socket; - } - } -?> -</pre> - This is probably the most highly factored answer as creation - is now moved into a small specialist class. - The networking factory can now be tested separately, but mocked - easily when we are testing the telnet class... -<pre> -class TelnetTest extends UnitTestCase { - ... - function testConnection() {<strong> - $socket = &new MockSocket($this); - ... - $network = &new MockNetwork($this); - $network->setReturnReference('createSocket', $socket); - $telnet = &new Telnet($network); - $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); - ...</strong> - } -} -</pre> - The downside is that we are adding a lot more classes to the - library. - Also we are passing a lot of factories around which will - make the code a little less intuitive. - The most flexible solution, but the most complex. - </p> - <p> - Is there a middle ground? - </p> - - <p> -<a class="target" name="creation"> -<h2>Protected factory method</h2> -</a> -</p> - <p> - There is a way we can circumvent the problem without creating - any new application classes, but it involves creating a subclass - when we do the actual testing. - Firstly we move the socket creation into its own method... -<pre> -<?php - require_once('socket.php'); - - class Telnet { - ... - function &connect($ip, $port, $username, $password) {<strong> - $socket = &$this->_createSocket($ip, $port);</strong> - $socket->read( ... ); - ... - }<strong> - - function &_createSocket($ip, $port) { - return new Socket($ip, $port); - }</strong> - } -?> -</pre> - This is the only change we make to the application code. - </p> - <p> - For the test case we have to create a subclass so that - we can intercept the socket creation... -<pre> -<strong>class TelnetTestVersion extends Telnet { - var $_mock; - - function TelnetTestVersion(&$mock) { - $this->_mock = &$mock; - $this->Telnet(); - } - - function &_createSocket() { - return $this->_mock; - } -}</strong> -</pre> - Here I have passed the mock in the constructor, but a - setter would have done just as well. - Note that the mock was set into the object variable - before the constructor was chained. - This is necessary in case the constructor calls - <span class="new_code">connect()</span>. - Otherwise it could get a null value from - <span class="new_code">_createSocket()</span>. - </p> - <p> - After the completion of all of this extra work the - actual test case is fairly easy. - We just test our new class instead... -<pre> -class TelnetTest extends UnitTestCase { - ... - function testConnection() {<strong> - $socket = &new MockSocket($this); - ... - $telnet = &new TelnetTestVersion($socket); - $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); - ...</strong> - } -} -</pre> - The new class is very simple of course. - It just sets up a return value, rather like a mock. - It would be nice if it also checked the incoming parameters - as well. - Just like a mock. - It seems we are likely to do this often, can - we automate the subclass creation? - </p> - - <p> -<a class="target" name="partial"> -<h2>A partial mock</h2> -</a> -</p> - <p> - Of course the answer is "yes" or I would have stopped writing - this by now! - The previous test case was a lot of work, but we can - generate the subclass using a similar approach to the mock objects. - </p> - <p> - Here is the partial mock version of the test... -<pre> -<strong>Mock::generatePartial( - 'Telnet', - 'TelnetTestVersion', - array('_createSocket'));</strong> - -class TelnetTest extends UnitTestCase { - ... - function testConnection() {<strong> - $socket = &new MockSocket($this); - ... - $telnet = &new TelnetTestVersion($this); - $telnet->setReturnReference('_createSocket', $socket); - $telnet->Telnet(); - $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); - ...</strong> - } -} -</pre> - The partial mock is a subclass of the original with - selected methods "knocked out" with test - versions. - The <span class="new_code">generatePartial()</span> call - takes three parameters: the class to be subclassed, - the new test class name and a list of methods to mock. - </p> - <p> - Instantiating the resulting objects is slightly tricky. - The only constructor parameter of a partial mock is - the unit tester reference. - As with the normal mock objects this is needed for sending - test results in response to checked expectations. - </p> - <p> - The original constructor is not run yet. - This is necessary in case the constructor is going to - make use of the as yet unset mocked methods. - We set any return values at this point and then run the - constructor with its normal parameters. - This three step construction of "new", followed - by setting up the methods, followed by running the constructor - proper is what distinguishes the partial mock code. - </p> - <p> - Apart from construction, all of the mocked methods have - the same features as mock objects and all of the unmocked - methods behave as before. - We can set expectations very easily... -<pre> -class TelnetTest extends UnitTestCase { - ... - function testConnection() { - $socket = &new MockSocket($this); - ... - $telnet = &new TelnetTestVersion($this); - $telnet->setReturnReference('_createSocket', $socket);<strong> - $telnet->expectOnce('_createSocket', array('127.0.0.1', 21));</strong> - $telnet->Telnet(); - $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); - ...<strong> - $telnet->tally();</strong> - } -} -</pre> - </p> - - <p> -<a class="target" name="less"> -<h2>Testing less than a class</h2> -</a> -</p> - <p> - The mocked out methods don't have to be factory methods, - they could be any sort of method. - In this way partial mocks allow us to take control of any part of - a class except the constructor. - We could even go as far as to mock every method - except one we actually want to test. - </p> - <p> - This last situation is all rather hypothetical, as I haven't - tried it. - I am open to the possibility, but a little worried that - forcing object granularity may be better for the code quality. - I personally use partial mocks as a way of overriding creation - or for occasional testing of the TemplateMethod pattern. - </p> - <p> - It's all going to come down to the coding standards of your - project to decide which mechanism you use. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> |