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 | |
parent | b6dfb6c447cf502e694d299dbda1b2e092c3312d (diff) |
move to tests
Diffstat (limited to 'test_tools/simpletest/docs/en')
14 files changed, 0 insertions, 5606 deletions
diff --git a/test_tools/simpletest/docs/en/authentication_documentation.html b/test_tools/simpletest/docs/en/authentication_documentation.html deleted file mode 100755 index 0623023c..00000000 --- a/test_tools/simpletest/docs/en/authentication_documentation.html +++ /dev/null @@ -1,320 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest documentation for testing log-in and authentication</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> -<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> -<span class="chosen">Authentication</span> -</li> -<li> -<a href="browser_documentation.html">Scriptable browser</a> -</li> -</ul> -</div> -</div> -<h1>Authentication documentation</h1> -<div class="content"> - - <p> - One of the trickiest, and yet most important, areas - of testing web sites is the security. - Testing these schemes is one of the core goals of - the SimpleTest web tester. - </p> - - <p> -<a class="target" name="basic"> -<h2>Basic HTTP authentication</h2> -</a> -</p> - <p> - If you fetch a page protected by basic authentication then - rather than receiving content, you will instead get a 401 - header. - We can illustrate this with this test... -<pre> -class AuthenticationTest extends WebTestCase {<strong> - function test401Header() { - $this->get('http://www.lastcraft.com/protected/'); - $this->showHeaders(); - }</strong> -} -</pre> - This allows us to see the challenge header... - <div class="demo"> - <h1>File test</h1> -<pre style="background-color: lightgray; color: black"> -HTTP/1.1 401 Authorization Required -Date: Sat, 18 Sep 2004 19:25:18 GMT -Server: Apache/1.3.29 (Unix) PHP/4.3.4 -WWW-Authenticate: Basic realm="SimpleTest basic authentication" -Connection: close -Content-Type: text/html; charset=iso-8859-1 -</pre> - <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete. - <strong>0</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div> - </div> - We are trying to get away from visual inspection though, and so SimpleTest - allows to make automated assertions against the challenge. - Here is a thorough test of our header... -<pre> -class AuthenticationTest extends WebTestCase { - function test401Header() { - $this->get('http://www.lastcraft.com/protected/');<strong> - $this->assertAuthentication('Basic'); - $this->assertResponse(401); - $this->assertRealm('SimpleTest basic authentication');</strong> - } -} -</pre> - Any one of these tests would normally do on it's own depending - on the amount of detail you want to see. - </p> - <p> - Most of the time we are not interested in testing the - authentication itself, but want to get past it to test - the pages underneath. - As soon as the challenge has been issued we can reply with - an authentication response... -<pre> -class AuthenticationTest extends WebTestCase { - function testAuthentication() { - $this->get('http://www.lastcraft.com/protected/');<strong> - $this->authenticate('Me', 'Secret');</strong> - $this->assertTitle(...); - } -} -</pre> - The username and password will now be sent with every - subsequent request to that directory and subdirectories. - You will have to authenticate again if you step outside - the authenticated directory, but SimpleTest is smart enough - to merge subdirectories into a common realm. - </p> - <p> - You can shortcut this step further by encoding the log in - details straight into the URL... -<pre> -class AuthenticationTest extends WebTestCase { - function testCanReadAuthenticatedPages() { - $this->get('http://<strong>Me:Secret@</strong>www.lastcraft.com/protected/'); - $this->assertTitle(...); - } -} -</pre> - If your username or password has special characters, then you - will have to URL encode them or the request will not be parsed - correctly. - Also this header will not be sent on subsequent requests if - you request a page with a fully qualified URL. - If you navigate with relative URLs though, the authentication - information will be preserved. - </p> - <p> - Only basic authentication is currently supported and this is - only really secure in tandem with HTTPS connections. - This is usually enough to protect test server from prying eyes, - however. - Digest authentication and NTLM authentication may be added - in the future. - </p> - - <p> -<a class="target" name="cookies"> -<h2>Cookies</h2> -</a> -</p> - <p> - Basic authentication doesn't give enough control over the - user interface for web developers. - More likely this functionality will be coded directly into - the web architecture using cookies and complicated timeouts. - </p> - <p> - Starting with a simple log-in form... -<pre> -<form> - Username: - <input type="text" name="u" value="" /><br /> - Password: - <input type="password" name="p" value="" /><br /> - <input type="submit" value="Log in" /> -</form> -</pre> - Which looks like... - </p> - <p> - <form class="demo"> - Username: - <input type="text" name="u" value=""> -<br> - Password: - <input type="password" name="p" value=""> -<br> - <input type="submit" value="Log in"> - </form> - </p> - <p> - Let's suppose that in fetching this page a cookie has been - set with a session ID. - We are not going to fill the form in yet, just test that - we are tracking the user. - Here is the test... -<pre> -class LogInTest extends WebTestCase { - function testSessionCookieSetBeforeForm() { - $this->get('http://www.my-site.com/login.php');<strong> - $this->assertCookie('SID');</strong> - } -} -</pre> - All we are doing is confirming that the cookie is set. - As the value is likely to be rather cryptic it's not - really worth testing this. - </p> - <p> - The rest of the test would be the same as any other form, - but we might want to confirm that we still have the same - cookie after log-in as before we entered. - We wouldn't want to lose track of this after all. - Here is a possible test for this... -<pre> -class LogInTest extends WebTestCase { - ... - function testSessionCookieSameAfterLogIn() { - $this->get('http://www.my-site.com/login.php');<strong> - $session = $this->getCookie('SID'); - $this->setField('u', 'Me'); - $this->setField('p', 'Secret'); - $this->clickSubmit('Log in'); - $this->assertWantedPattern('/Welcome Me/'); - $this->assertCookie('SID', $session);</strong> - } -} -</pre> - This confirms that the session identifier is maintained - afer log-in. - </p> - <p> - We could even attempt to spoof our own system by setting - arbitrary cookies to gain access... -<pre> -class LogInTest extends WebTestCase { - ... - function testSessionCookieSameAfterLogIn() { - $this->get('http://www.my-site.com/login.php');<strong> - $this->setCookie('SID', 'Some other session'); - $this->get('http://www.my-site.com/restricted.php');</strong> - $this->assertWantedPattern('/Access denied/'); - } -} -</pre> - Is your site protected from this attack? - </p> - - <p> -<a class="target" name="session"> -<h2>Browser sessions</h2> -</a> -</p> - <p> - If you are testing an authentication system a critical piece - of behaviour is what happens when a user logs back in. - We would like to simulate closing and reopening a browser... -<pre> -class LogInTest extends WebTestCase { - ... - function testLoseAuthenticationAfterBrowserClose() { - $this->get('http://www.my-site.com/login.php'); - $this->setField('u', 'Me'); - $this->setField('p', 'Secret'); - $this->clickSubmit('Log in'); - $this->assertWantedPattern('/Welcome Me/');<strong> - - $this->restart(); - $this->get('http://www.my-site.com/restricted.php'); - $this->assertWantedPattern('/Access denied/');</strong> - } -} -</pre> - The <span class="new_code">WebTestCase::restart()</span> method will - preserve cookies that have unexpired timeouts, but throw away - those that are temporary or expired. - You can optionally specify the time and date that the restart - happened. - </p> - <p> - Expiring cookies can be a problem. - After all, if you have a cookie that expires after an hour, - you don't want to stall the test for an hour while the - cookie passes it's timeout. - </p> - <p> - To push the cookies over the hour limit you can age them - before you restart the session... -<pre> -class LogInTest extends WebTestCase { - ... - function testLoseAuthenticationAfterOneHour() { - $this->get('http://www.my-site.com/login.php'); - $this->setField('u', 'Me'); - $this->setField('p', 'Secret'); - $this->clickSubmit('Log in'); - $this->assertWantedPattern('/Welcome Me/'); - <strong> - $this->ageCookies(3600);</strong> - $this->restart(); - $this->get('http://www.my-site.com/restricted.php'); - $this->assertWantedPattern('/Access denied/'); - } -} -</pre> - After the restart it will appear that cookies are an - hour older and any that pass their expiry will have - disappeared. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/browser_documentation.html b/test_tools/simpletest/docs/en/browser_documentation.html deleted file mode 100755 index ef54aaea..00000000 --- a/test_tools/simpletest/docs/en/browser_documentation.html +++ /dev/null @@ -1,386 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest documentation for the scriptable web browser component</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> -<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> -<span class="chosen">Scriptable browser</span> -</li> -</ul> -</div> -</div> -<h1>PHP Scriptable Web Browser</h1> -<div class="content"> - - <p> - SimpleTest's web browser component can be used not just - outside of the <span class="new_code">WebTestCase</span> class, but also - independently of the SimpleTest framework itself. - </p> - - <p> -<a class="target" name="scripting"> -<h2>The Scriptable Browser</h2> -</a> -</p> - <p> - You can use the web browser in PHP scripts to confirm - services are up and running, or to extract information - from them at a regular basis. - For example, here is a small script to extract the current number of - open PHP 5 bugs from the <a href="http://www.php.net/">PHP web site</a>... -<pre> -<strong><?php - require_once('simpletest/browser.php'); - - $browser = &new SimpleBrowser(); - $browser->get('http://php.net/'); - $browser->clickLink('reporting bugs'); - $browser->clickLink('statistics'); - $page = $browser->clickLink('PHP 5 bugs only'); - preg_match('/status=Open.*?by=Any.*?(\d+)<\/a>/', $page, $matches); - print $matches[1]; -?></strong> -</pre> - There are simpler methods to do this particular example in PHP - of course. - For example you can just use the PHP <span class="new_code">file()</span> - command against what here is a pretty fixed page. - However, using the web browser for scripts allows authentication, - correct handling of cookies, automatic loading of frames, redirects, - form submission and the ability to examine the page headers. - Such methods are fragile against a site that is constantly - evolving and you would want a more direct way of accessing - data in a permanent set up, but for simple tasks this can provide - a very rapid solution. - </p> - <p> - All of the navigation methods used in the - <a href="web_tester_documentation.html">WebTestCase</a> - are present in the <span class="new_code">SimpleBrowser</span> class, but - the assertions are replaced with simpler accessors. - Here is a full list of the page navigation methods... - <table> -<tbody> - <tr> -<td><span class="new_code">addHeader($header)</span></td><td>Adds a header to every fetch</td> -</tr> - <tr> -<td><span class="new_code">useProxy($proxy, $username, $password)</span></td><td>Use this proxy from now on</td> -</tr> - <tr> -<td><span class="new_code">head($url, $parameters)</span></td><td>Perform a HEAD request</td> -</tr> - <tr> -<td><span class="new_code">get($url, $parameters)</span></td><td>Fetch a page with GET</td> -</tr> - <tr> -<td><span class="new_code">post($url, $parameters)</span></td><td>Fetch a page with POST</td> -</tr> - <tr> -<td><span class="new_code">clickLink($label)</span></td><td>Follows a link by label</td> -</tr> - <tr> -<td><span class="new_code">isLink($label)</span></td><td>See if a link is present by label</td> -</tr> - <tr> -<td><span class="new_code">clickLinkById($id)</span></td><td>Follows a link by attribute</td> -</tr> - <tr> -<td><span class="new_code">isLinkById($id)</span></td><td>See if a link is present by attribut</td> -</tr> - <tr> -<td><span class="new_code">getUrl()</span></td><td>Current URL of page or frame</td> -</tr> - <tr> -<td><span class="new_code">getTitle()</span></td><td>Page title</td> -</tr> - <tr> -<td><span class="new_code">getContent()</span></td><td>Raw page or frame</td> -</tr> - <tr> -<td><span class="new_code">getContentAsText()</span></td><td>HTML removed except for alt text</td> -</tr> - <tr> -<td><span class="new_code">retry()</span></td><td>Repeat the last request</td> -</tr> - <tr> -<td><span class="new_code">back()</span></td><td>Use the browser back button</td> -</tr> - <tr> -<td><span class="new_code">forward()</span></td><td>Use the browser forward button</td> -</tr> - <tr> -<td><span class="new_code">authenticate($username, $password)</span></td><td>Retry page or frame after a 401 response</td> -</tr> - <tr> -<td><span class="new_code">restart($date)</span></td><td>Restarts the browser for a new session</td> -</tr> - <tr> -<td><span class="new_code">ageCookies($interval)</span></td><td>Ages the cookies by the specified time</td> -</tr> - <tr> -<td><span class="new_code">setCookie($name, $value)</span></td><td>Sets an additional cookie</td> -</tr> - <tr> -<td><span class="new_code">getCookieValue($host, $path, $name)</span></td><td>Reads the most specific cookie</td> -</tr> - <tr> -<td><span class="new_code">getCurrentCookieValue($name)</span></td><td>Reads cookie for the current context</td> -</tr> - </tbody> -</table> - The methods <span class="new_code">SimpleBrowser::useProxy()</span> and - <span class="new_code">SimpleBrowser::addHeader()</span> are special. - Once called they continue to apply to all subsequent fetches. - </p> - <p> - Navigating forms is similar to the - <a href="form_testing_documentation.html">WebTestCase form navigation</a>... - <table> -<tbody> - <tr> -<td><span class="new_code">setField($name, $value)</span></td><td>Sets all form fields with that name</td> -</tr> - <tr> -<td><span class="new_code">setFieldById($id, $value)</span></td><td>Sets all form fields with that id</td> -</tr> - <tr> -<td><span class="new_code">getField($name)</span></td><td>Accessor for a form element value</td> -</tr> - <tr> -<td><span class="new_code">getFieldById($id)</span></td><td>Accessor for a form element value</td> -</tr> - <tr> -<td><span class="new_code">clickSubmit($label)</span></td><td>Submits form by button label</td> -</tr> - <tr> -<td><span class="new_code">clickSubmitByName($name)</span></td><td>Submits form by button attribute</td> -</tr> - <tr> -<td><span class="new_code">clickSubmitById($id)</span></td><td>Submits form by button attribute</td> -</tr> - <tr> -<td><span class="new_code">clickImage($label, $x, $y)</span></td><td>Clicks the image by alt text</td> -</tr> - <tr> -<td><span class="new_code">clickImageByName($name, $x, $y)</span></td><td>Clicks the image by attribute</td> -</tr> - <tr> -<td><span class="new_code">clickImageById($id, $x, $y)</span></td><td>Clicks the image by attribute</td> -</tr> - <tr> -<td><span class="new_code">submitFormById($id)</span></td><td>Submits by the form tag attribute</td> -</tr> - </tbody> -</table> - At the moment there aren't any methods to list available forms - and fields. - This will probably be added to later versions of SimpleTest. - </p> - <p> - Within a page, individual frames can be selected. - If no selection is made then all the frames are merged together - in one large conceptual page. - The content of the current page will be a concatenation of all of the - frames in the order that they were specified in the "frameset" - tags. - <table> -<tbody> - <tr> -<td><span class="new_code">getFrames()</span></td><td>A dump of the current frame structure</td> -</tr> - <tr> -<td><span class="new_code">getFrameFocus()</span></td><td>Current frame label or index</td> -</tr> - <tr> -<td><span class="new_code">setFrameFocusByIndex($choice)</span></td><td>Select a frame numbered from 1</td> -</tr> - <tr> -<td><span class="new_code">setFrameFocus($name)</span></td><td>Select frame by label</td> -</tr> - <tr> -<td><span class="new_code">clearFrameFocus()</span></td><td>Treat all the frames as a single page</td> -</tr> - </tbody> -</table> - When focused on a single frame, the content will come from - that frame only. - This includes links to click and forms to submit. - </p> - - <p> -<a class="target" name="debug"> -<h2>What went wrong?</h2> -</a> -</p> - <p> - All of this functionality is great when we actually manage to fetch pages, - but that doesn't always happen. - To help figure out what went wrong, the browser has some methods to - aid in debugging... - <table> -<tbody> - <tr> -<td><span class="new_code">setConnectionTimeout($timeout)</span></td><td>Close the socket on overrun</td> -</tr> - <tr> -<td><span class="new_code">getRequest()</span></td><td>Raw request header of page or frame</td> -</tr> - <tr> -<td><span class="new_code">getHeaders()</span></td><td>Raw response header of page or frame</td> -</tr> - <tr> -<td><span class="new_code">getTransportError()</span></td><td>Any socket level errors in the last fetch</td> -</tr> - <tr> -<td><span class="new_code">getResponseCode()</span></td><td>HTTP response of page or frame</td> -</tr> - <tr> -<td><span class="new_code">getMimeType()</span></td><td>Mime type of page or frame</td> -</tr> - <tr> -<td><span class="new_code">getAuthentication()</span></td><td>Authentication type in 401 challenge header</td> -</tr> - <tr> -<td><span class="new_code">getRealm()</span></td><td>Authentication realm in 401 challenge header</td> -</tr> - <tr> -<td><span class="new_code">setMaximumRedirects($max)</span></td><td>Number of redirects before page is loaded anyway</td> -</tr> - <tr> -<td><span class="new_code">setMaximumNestedFrames($max)</span></td><td>Protection against recursive framesets</td> -</tr> - <tr> -<td><span class="new_code">ignoreFrames()</span></td><td>Disables frames support</td> -</tr> - <tr> -<td><span class="new_code">useFrames()</span></td><td>Enables frames support</td> -</tr> - </tbody> -</table> - The methods <span class="new_code">SimpleBrowser::setConnectionTimeout()</span> - <span class="new_code">SimpleBrowser::setMaximumRedirects()</span>, - <span class="new_code">SimpleBrowser::setMaximumNestedFrames()</span>, - <span class="new_code">SimpleBrowser::ignoreFrames()</span> and - <span class="new_code">SimpleBrowser::useFrames()</span> continue to apply - to every subsequent request. - The other methods are frames aware. - This means that if you have an individual frame that is not - loading, navigate to it using <span class="new_code">SimpleBrowser::setFrameFocus()</span> - and you can then use <span class="new_code">SimpleBrowser::getRequest()</span>, etc to - see what happened. - </p> - - <p> -<a class="target" name="unit"> -<h2>Complex unit tests with multiple browsers</h2> -</a> -</p> - <p> - Anything that could be done in a - <a href="web_tester_documentation.html">WebTestCase</a> can - now be done in a <a href="unit_tester_documentation.html">UnitTestCase</a>. - This means that we can freely mix domain object testing with the - web interface... -<pre> -<strong> -class TestOfRegistration extends UnitTestCase { - function testNewUserAddedToAuthenticator() {</strong> - $browser = &new SimpleBrowser(); - $browser->get('http://my-site.com/register.php'); - $browser->setField('email', 'me@here'); - $browser->setField('password', 'Secret'); - $browser->clickSubmit('Register'); - <strong> - $authenticator = &new Authenticator(); - $member = &$authenticator->findByEmail('me@here'); - $this->assertEqual($member->getPassword(), 'Secret'); - } -}</strong> -</pre> - While this may be a useful temporary expediency, I am not a fan - of this type of testing. - The testing has cut across application layers, make it twice as - likely it will need refactoring when the code changes. - </p> - <p> - A more useful case of where using the browser directly can be helpful - is where the <span class="new_code">WebTestCase</span> cannot cope. - An example is where two browsers are needed at the same time. - </p> - <p> - For example, say we want to disallow multiple simultaneous - usage of a site with the same username. - This test case will do the job... -<pre> -class TestOfSecurity extends UnitTestCase { - function testNoMultipleLoginsFromSameUser() {<strong> - $first = &new SimpleBrowser(); - $first->get('http://my-site.com/login.php'); - $first->setField('name', 'Me'); - $first->setField('password', 'Secret'); - $first->clickSubmit('Enter'); - $this->assertEqual($first->getTitle(), 'Welcome'); - - $second = &new SimpleBrowser(); - $second->get('http://my-site.com/login.php'); - $second->setField('name', 'Me'); - $second->setField('password', 'Secret'); - $second->clickSubmit('Enter'); - $this->assertEqual($second->getTitle(), 'Access Denied');</strong> - } -} -</pre> - You can also use the <span class="new_code">SimpleBrowser</span> class - directly when you want to write test cases using a different - test tool than SimpleTest. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/docs.css b/test_tools/simpletest/docs/en/docs.css deleted file mode 100755 index 93226cd7..00000000 --- a/test_tools/simpletest/docs/en/docs.css +++ /dev/null @@ -1,84 +0,0 @@ -body { - padding-left: 3%; - padding-right: 3%; -} -pre { - font-family: courier; - font-size: 80%; - border: 1px solid; - background-color: #cccccc; - padding: 5px; - margin-left: 5%; - margin-right: 8%; -} -.code, .new_code, pre.new_code { - font-weight: bold; -} -div.copyright { - font-size: 80%; - color: gray; -} -div.copyright a { - color: gray; -} -ul.api { - padding-left: 0em; - padding-right: 25%; -} -ul.api li { - margin-top: 0.2em; - margin-bottom: 0.2em; - list-style: none; - text-indent: -3em; - padding-left: 3em; -} -div.demo { - border: 4px ridge; - border-color: gray; - padding: 10px; - margin: 5px; - margin-left: 20px; - margin-right: 40px; - background-color: white; -} -div.demo span.fail { - color: red; -} -div.demo span.pass { - color: green; -} -div.demo h1 { - font-size: 12pt; - text-align: left; - font-weight: bold; -} -table { - border: 2px outset; - border-color: gray; - background-color: white; - margin: 5px; - margin-left: 5%; - margin-right: 5%; -} -td { - font-size: 80%; -} -.shell { - color: white; -} -pre.shell { - border: 4px ridge; - border-color: gray; - padding: 10px; - margin: 5px; - margin-left: 20px; - margin-right: 40px; - background-color: black; -} -form.demo { - background-color: lightgray; - border: 4px outset; - border-color: lightgray; - padding: 10px; - margin-right: 40%; -} diff --git a/test_tools/simpletest/docs/en/expectation_documentation.html b/test_tools/simpletest/docs/en/expectation_documentation.html deleted file mode 100755 index 0165988c..00000000 --- a/test_tools/simpletest/docs/en/expectation_documentation.html +++ /dev/null @@ -1,356 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title> - Extending the SimpleTest unit tester with additional expectation classes - </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> -<a href="partial_mocks_documentation.html">Partial mocks</a> -</li> -<li> -<a href="reporter_documentation.html">Reporting</a> -</li> -<li> -<span class="chosen">Expectations</span> -</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>Expectation documentation</h1> -<div class="content"> - <p> -<a class="target" name="mock"> -<h2>More control over mock objects</h2> -</a> -</p> - <p> - The default behaviour of the - <a href="mock_objects_documentation.html">mock objects</a> - in - <a href="http://sourceforge.net/projects/simpletest/">SimpleTest</a> - is either an identical match on the argument or to allow any argument at all. - For almost all tests this is sufficient. - Sometimes, though, you want to weaken a test case. - </p> - <p> - One place where a test can be too tightly coupled is with - text matching. - Suppose we have a component that outputs a helpful error - message when something goes wrong. - You want to test that the correct error was sent, but the actual - text may be rather long. - If you test for the text exactly, then every time the exact wording - of the message changes, you will have to go back and edit the test suite. - </p> - <p> - For example, suppose we have a news service that has failed - to connect to its remote source. -<pre> -<strong>class NewsService { - ... - function publish(&$writer) { - if (! $this->isConnected()) { - $writer->write('Cannot connect to news service "' . - $this->_name . '" at this time. ' . - 'Please try again later.'); - } - ... - } -}</strong> -</pre> - Here it is sending its content to a - <span class="new_code">Writer</span> class. - We could test this behaviour with a - <span class="new_code">MockWriter</span> like so... -<pre> -class TestOfNewsService extends UnitTestCase { - ... - function testConnectionFailure() {<strong> - $writer = &new MockWriter($this); - $writer->expectOnce('write', array( - 'Cannot connect to news service ' . - '"BBC News" at this time. ' . - 'Please try again later.')); - - $service = &new NewsService('BBC News'); - $service->publish($writer); - - $writer->tally();</strong> - } -} -</pre> - This is a good example of a brittle test. - If we decide to add additional instructions, such as - suggesting an alternative news source, we will break - our tests even though no underlying functionality - has been altered. - </p> - <p> - To get around this, we would like to do a regular expression - test rather than an exact match. - We can actually do this with... -<pre> -class TestOfNewsService extends UnitTestCase { - ... - function testConnectionFailure() { - $writer = &new MockWriter($this);<strong> - $writer->expectOnce( - 'write', - array(new WantedPatternExpectation('/cannot connect/i')));</strong> - - $service = &new NewsService('BBC News'); - $service->publish($writer); - - $writer->tally(); - } -} -</pre> - Instead of passing in the expected parameter to the - <span class="new_code">MockWriter</span> we pass an - expectation class called - <span class="new_code">WantedPatternExpectation</span>. - The mock object is smart enough to recognise this as special - and to treat it differently. - Rather than simply comparing the incoming argument to this - object, it uses the expectation object itself to - perform the test. - </p> - <p> - The <span class="new_code">WantedPatternExpectation</span> takes - the regular expression to match in its constructor. - Whenever a comparison is made by the <span class="new_code">MockWriter</span> - against this expectation class, it will do a - <span class="new_code">preg_match()</span> with this pattern. - With our test case above, as long as "cannot connect" - appears in the text of the string, the mock will issue a pass - to the unit tester. - The rest of the text does not matter. - </p> - <p> - The possible expectation classes are... - <table> -<tbody> - <tr> -<td><span class="new_code">EqualExpectation</span></td><td>An equality, rather than the stronger identity comparison</td> -</tr> - <tr> -<td><span class="new_code">NotEqualExpectation</span></td><td>An inequality comparison</td> -</tr> - <tr> -<td><span class="new_code">IndenticalExpectation</span></td><td>The default mock object check which must match exactly</td> -</tr> - <tr> -<td><span class="new_code">NotIndenticalExpectation</span></td><td>Inverts the mock object logic</td> -</tr> - <tr> -<td><span class="new_code">WantedPatternExpectation</span></td><td>Uses a Perl Regex to match a string</td> -</tr> - <tr> -<td><span class="new_code">NoUnwantedPatternExpectation</span></td><td>Passes only if failing a Perl Regex</td> -</tr> - <tr> -<td><span class="new_code">IsAExpectation</span></td><td>Checks the type or class name only</td> -</tr> - <tr> -<td><span class="new_code">NotAExpectation</span></td><td>Opposite of the <span class="new_code">IsAExpectation</span></td> -</tr> - <tr> -<td><span class="new_code">MethodExistsExpectation</span></td><td>Checks a method is available on an object</td> -</tr> - </tbody> -</table> - Most take the expected value in the constructor. - The exceptions are the pattern matchers, which take a regular expression, - and the <span class="new_code">IsAExpectation</span> and <span class="new_code">NotAExpectation</span> which takes a type - or class name as a string. - </p> - - <p> -<a class="target" name="behaviour"> -<h2>Using expectations to control stubs</h2> -</a> -</p> - <p> - The expectation classes can be used not just for sending assertions - from mock objects, but also for selecting behaviour for either - the - <a href="mock_objects_documentation.html">mock objects</a> - or the - <a href="server_stubs_documentation.html">server stubs</a>. - Anywhere a list of arguments is given, a list of expectation objects - can be inserted instead. - </p> - <p> - Suppose we want an authorisation server stub to simulate a successful login - only if it receives a valid session object. - We can do this as follows... -<pre> -Stub::generate('Authorisation'); -<strong> -$authorisation = new StubAuthorisation(); -$authorisation->setReturnValue( - 'isAllowed', - true, - array(new IsAExpectation('Session', 'Must be a session'))); -$authorisation->setReturnValue('isAllowed', false);</strong> -</pre> - We have set the default stub behaviour to return false when - <span class="new_code">isAllowed</span> is called. - When we call the method with a single parameter that - is a <span class="new_code">Session</span> object, it will return true. - We have also added a second parameter as a message. - This will be displayed as part of the mock object - failure message if this expectation is the cause of - a failure. - </p> - <p> - This kind of sophistication is rarely useful, but is included for - completeness. - </p> - - <p> -<a class="target" name="extending"> -<h2>Creating your own expectations</h2> -</a> -</p> - <p> - The expectation classes have a very simple structure. - So simple that it is easy to create your own versions for - commonly used test logic. - </p> - <p> - As an example here is the creation of a class to test for - valid IP addresses. - In order to work correctly with the stubs and mocks the new - expectation class should extend - <span class="new_code">SimpleExpectation</span>... -<pre> -<strong>class ValidIp extends SimpleExpectation { - - function test($ip) { - return (ip2long($ip) != -1); - } - - function testMessage($ip) { - return "Address [$ip] should be a valid IP address"; - } -}</strong> -</pre> - There are only two methods to implement. - The <span class="new_code">test()</span> method should - evaluate to true if the expectation is to pass, and - false otherwise. - The <span class="new_code">testMessage()</span> method - should simply return some helpful text explaining the test - that was carried out. - </p> - <p> - This class can now be used in place of the earlier expectation - classes. - </p> - - <p> -<a class="target" name="unit"> -<h2>Under the bonnet of the unit tester</h2> -</a> -</p> - <p> - The <a href="http://sourceforge.net/projects/simpletest/">SimpleTest unit testing framework</a> - also uses the expectation classes internally for the - <a href="unit_test_documentation.html">UnitTestCase class</a>. - We can also take advantage of these mechanisms to reuse our - homebrew expectation classes within the test suites directly. - </p> - <p> - The most crude way of doing this is to use the - <span class="new_code">SimpleTest::assertExpectation()</span> method to - test against it directly... -<pre> -<strong>class TestOfNetworking extends UnitTestCase { - ... - function testGetValidIp() { - $server = &new Server(); - $this->assertExpectation( - new ValidIp(), - $server->getIp(), - 'Server IP address->%s'); - } -}</strong> -</pre> - This is a little untidy compared with our usual - <span class="new_code">assert...()</span> syntax. - </p> - <p> - For such a simple case we would normally create a - separate assertion method on our test case rather - than bother using the expectation class. - If we pretend that our expectation is a little more - complicated for a moment, so that we want to reuse it, - we get... -<pre> -class TestOfNetworking extends UnitTestCase { - ...<strong> - function assertValidIp($ip, $message = '%s') { - $this->assertExpectation(new ValidIp(), $ip, $message); - }</strong> - - function testGetValidIp() { - $server = &new Server();<strong> - $this->assertValidIp( - $server->getIp(), - 'Server IP address->%s');</strong> - } -} -</pre> - It is unlikely we would ever need this degree of control - over the testing machinery. - It is rare to need the expectations for more than pattern - matching. - Also, complex expectation classes could make the tests - harder to read and debug. - These mechanisms are really of most use to authors of systems - that will extend the test framework to create their own tool set. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/form_testing_documentation.html b/test_tools/simpletest/docs/en/form_testing_documentation.html deleted file mode 100755 index b1e15b3d..00000000 --- a/test_tools/simpletest/docs/en/form_testing_documentation.html +++ /dev/null @@ -1,277 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>Simple Test documentation for testing HTML forms</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> -<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> -<span class="chosen">Testing forms</span> -</li> -<li> -<a href="authentication_documentation.html">Authentication</a> -</li> -<li> -<a href="browser_documentation.html">Scriptable browser</a> -</li> -</ul> -</div> -</div> -<h1>Form testing documentation</h1> -<div class="content"> - <p> -<a class="target" name="submit"> -<h2>Submitting a simple form</h2> -</a> -</p> - <p> - When a page is fetched by the <span class="new_code">WebTestCase</span> - using <span class="new_code">get()</span> or - <span class="new_code">post()</span> the page content is - automatically parsed. - This results in any form controls that are inside <form> tags - being available from within the test case. - For example, if we have this snippet of HTML... -<pre> -<form> - <input type="text" name="a" value="A default" /> - <input type="submit" value="Go" /> -</form> -</pre> - Which looks like this... - </p> - <p> - <form class="demo"> - <input type="text" name="a" value="A default"> - <input type="submit" value="Go"> - </form> - </p> - <p> - We can navigate to this code, via the - <a href="http://www.lastcraft.com/form_testing_documentation.php">LastCraft</a> - site, with the following test... -<pre> -class SimpleFormTests extends WebTestCase { - <strong> - function testDefaultValue() { - $this->get('http://www.lastcraft.com/form_testing_documentation.php'); - $this->assertField('a', 'A default'); - }</strong> -} -</pre> - Immediately after loading the page all of the HTML controls are set at - their default values just as they would appear in the web browser. - The assertion tests that a HTML widget exists in the page with the - name "a" and that it is currently set to the value - "A default" - </p> - <p> - We could submit the form straight away, but first we'll change - the value of the text field and only then submit it... -<pre> -class SimpleFormTests extends WebTestCase { - - function testDefaultValue() { - $this->get('http://www.my-site.com/'); - $this->assertField('a', 'A default');<strong> - $this->setField('a', 'New value'); - $this->clickSubmit('Go');</strong> - } -} -</pre> - Because we didn't specify a method attribute on the form tag, and - didn't specify an action either, the test case will follow - the usual browser behaviour of submitting the form data as a <em>GET</em> - request back to the same location. - SimpleTest tries to emulate typical browser behaviour as much as possible, - rather than attempting to catch missing attributes on tags. - This is because the target of the testing framework is the PHP application - logic, not syntax or other errors in the HTML code. - For HTML errors, other tools such as - <a href="http://www.w3.org/People/Raggett/tidy/">HTMLTidy</a> should be used. - </p> - <p> - If a field is not present in any form, or if an option is unavailable, - then <span class="new_code">WebTestCase::setField()</span> will return - <span class="new_code">false</span>. - For example, suppose we wish to verify that a "Superuser" - option is not present in this form... -<pre> -<strong>Select type of user to add:</strong> -<select name="type"> - <option>Subscriber</option> - <option>Author</option> - <option>Administrator</option> -</select> -</pre> - Which looks like... - </p> - <p> - <form class="demo"> - <strong>Select type of user to add:</strong> - <select name="type"> - <option>Subscriber</option> - <option>Author</option> - <option>Administrator</option> - </select> - </form> - </p> - <p> - The following test will confirm it... -<pre> -class SimpleFormTests extends WebTestCase { - ... - function testNoSuperuserChoiceAvailable() {<strong> - $this->get('http://www.lastcraft.com/form_testing_documentation.php'); - $this->assertFalse($this->setField('type', 'Superuser'));</strong> - } -} -</pre> - The selection will not be changed on a failure to set - a widget value. - </p> - <p> - Here is the full list of widgets currently supported... - <ul> - <li>Text fields, including hidden and password fields.</li> - <li>Submit buttons including the button tag, although not yet reset buttons</li> - <li>Text area. This includes text wrapping behaviour.</li> - <li>Checkboxes, including multiple checkboxes in the same form.</li> - <li>Drop down selections, including multiple selects.</li> - <li>Radio buttons.</li> - <li>Images.</li> - </ul> - </p> - <p> - Although most standard HTML widgets are catered for by <em>SimpleTest</em>'s - built in parser, it is unlikely that JavaScript will be implemented - anytime soon. - </p> - - <p> -<a class="target" name="multiple"> -<h2>Fields with multiple values</h2> -</a> -</p> - <p> - SimpleTest can cope with two types of multivalue controls: Multiple - selection drop downs, and multiple checkboxes with the same name - within a form. - The multivalue nature of these means that setting and testing - are slightly different. - Using checkboxes as an example... -<pre> -<form class="demo"> - <strong>Create privileges allowed:</strong> - <input type="checkbox" name="crud" value="c" checked><br> - <strong>Retrieve privileges allowed:</strong> - <input type="checkbox" name="crud" value="r" checked><br> - <strong>Update privileges allowed:</strong> - <input type="checkbox" name="crud" value="u" checked><br> - <strong>Destroy privileges allowed:</strong> - <input type="checkbox" name="crud" value="d" checked><br> - <input type="submit" value="Enable Privileges"> -</form> -</pre> - Which renders as... - </p> - <p> - <form class="demo"> - <strong>Create privileges allowed:</strong> - <input type="checkbox" name="crud" value="c" checked> -<br> - <strong>Retrieve privileges allowed:</strong> - <input type="checkbox" name="crud" value="r" checked> -<br> - <strong>Update privileges allowed:</strong> - <input type="checkbox" name="crud" value="u" checked> -<br> - <strong>Destroy privileges allowed:</strong> - <input type="checkbox" name="crud" value="d" checked> -<br> - <input type="submit" value="Enable Privileges"> - </form> - </p> - <p> - If we wish to disable all but the retrieval privileges and - submit this information we can do it like this... -<pre> -class SimpleFormTests extends WebTestCase { - ...<strong> - function testDisableNastyPrivileges() { - $this->get('http://www.lastcraft.com/form_testing_documentation.php'); - $this->assertField('crud', array('c', 'r', 'u', 'd')); - $this->setField('crud', array('r')); - $this->clickSubmit('Enable Privileges'); - }</strong> -} -</pre> - Instead of setting the field to a single value, we give it a list - of values. - We do the same when testing expected values. - We can then write other test code to confirm the effect of this, perhaps - by logging in as that user and attempting an update. - </p> - <p> - <a class="target" name="raw"> -<h2>Raw posting</h2> -</a> - </p> - <p> - If you want to test a form handler, but have not yet written - or do not have access to the form itself, you can create a - form submission by hand. -<pre> -class SimpleFormTests extends WebTestCase { - ...<strong> - function testAttemptedHack() { - $this->post( - 'http://www.my-site.com/add_user.php', - array('type' => 'superuser')); - $this->assertNoUnwantedPattern('/user created/i'); - }</strong> -} -</pre> - By adding data to the <span class="new_code">WebTestCase::post()</span> - method, we are attempting to fetch the page as a form submission. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/group_test_documentation.html b/test_tools/simpletest/docs/en/group_test_documentation.html deleted file mode 100755 index adbc66ef..00000000 --- a/test_tools/simpletest/docs/en/group_test_documentation.html +++ /dev/null @@ -1,357 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest for PHP group test 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> -<span class="chosen">Group tests</span> -</li> -<li> -<a href="server_stubs_documentation.html">Server stubs</a> -</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>Group Test documentation</h1> -<div class="content"> - <p> -<a class="target" name="group"> -<h2>Grouping tests</h2> -</a> -</p> - <p> - To run test cases as part of a group the test cases should really - be placed in files without the runner code... -<pre> -<strong><?php - require_once('../classes/io.php'); - - class FileTester extends UnitTestCase { - ... - } - - class SocketTester extends UnitTestCase { - ... - } -?></strong> -</pre> - As many cases as needed can appear in a single file. - They should include any code they need, such as the library - being tested, but none of the simple test libraries. - </p> - <p> - If you have extended any test cases, you can include them - as well. -<pre> -<?php - require_once('../classes/io.php'); -<strong> - class MyFileTestCase extends UnitTestCase { - ... - } - SimpleTestOptions::ignore('MyFileTestCase');</strong> - - class FileTester extends MyFileTestCase { - ... - } - - class SocketTester extends UnitTestCase { - ... - } -?> -</pre> - The <span class="new_code">FileTester</span> class does - not contain any actual tests, but is a base class for other - test cases. - For this reason we use the - <span class="new_code">SimpleTestOptions::ignore()</span> directive - to tell the upcoming group test to ignore it. - This directive can appear anywhere in the file and works - when a whole file of test cases is loaded (see below). - We will call this sample <em>file_test.php</em>. - </p> - <p> - Next we create a group test file, called say <em>group_test.php</em>. - You will think of a better name I am sure. - We will add the test file using a safe method... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php');<strong> - require_once('file_test.php'); - - $test = &new GroupTest('All file tests'); - $test->addTestCase(new FileTestCase()); - $test->run(new HtmlReporter());</strong> -?> -</pre> - This instantiates the test case before the test suite is - run. - This could get a little expensive with a large number of test - cases, so another method is provided that will only - instantiate the class when it is needed... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - require_once('file_test.php'); - - $test = &new GroupTest('All file tests');<strong> - $test->addTestClass('FileTestCase');</strong> - $test->run(new HtmlReporter()); -?> -</pre> - The problem with this method is that for every test case - that we add we will have - to <span class="new_code">require_once()</span> the test code - file and manually instantiate each and every test case. - We can save a lot of typing with... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - - $test = &new GroupTest('All file tests');<strong> - $test->addTestFile('file_test.php');</strong> - $test->run(new HtmlReporter()); -?&gt; -</pre> - What happens here is that the <span class="new_code">GroupTest</span> - class has done the <span class="new_code">require_once()</span> - for us. - It then checks to see if any new test case classes - have been created by the new file and automatically adds - them to the group test. - Now all we have to do is add each new file. - </p> - <p> - There are two things that could go wrong and which require care... - <ol> - <li> - The file could already have been parsed by PHP and so no - new classes will have been added. You should make - sure that the test cases are only included in this file - and no others. - </li> - <li> - New test case extension classes that get included will be - placed in the group test and run also. - You will need to add a <span class="new_code">SimpleTestOptions::ignore()</span> - directive for these classes or make sure that they are included - before the <span class="new_code">GroupTest::addTestFile()</span> - line. - </li> - </ol> - </p> - - <p> -<a class="target" name="higher"> -<h2>Higher groupings</h2> -</a> -</p> - <p> - The above method places all of the test cases into one large group. - For larger projects though this may not be flexible enough; you - may want to group the tests in all sorts of ways. - </p> - <p> - To get a more flexible group test we can subclass - <span class="new_code">GroupTest</span> and then instantiate it as needed... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - <strong> - class FileGroupTest extends GroupTest { - function FileGroupTest() { - $this->GroupTest('All file tests'); - $this->addTestFile('file_test.php'); - } - }</strong> -?> -</pre> - This effectively names the test in the constructor and then - adds our test cases and a single group below. - Of course we can add more than one group at this point. - We can now invoke the tests from a separate runner file... -<pre> -<?php - require_once('file_group_test.php'); - <strong> - $test = &new FileGroupTest(); - $test->run(new HtmlReporter());</strong> -?> -</pre> - ...or we can group them into even larger group tests... -<pre> -<?php - require_once('file_group_test.php'); - <strong> - $test = &new BigGroupTest('Big group'); - $test->addTestCase(new FileGroupTest()); - $test->addTestCase(...); - $test->run(new HtmlReporter());</strong> -?> -</pre> - If we still wish to run the original group test and we - don't want all of these little runner files, we can - put the test runner code around guard bars when we create - each group. -<pre> -<?php - class FileGroupTest extends GroupTest { - function FileGroupTest() { - $this->GroupTest('All file tests'); - $test->addTestFile('file_test.php'); - } - } - <strong> - if (! defined('RUNNER')) { - define('RUNNER', true);</strong> - $test = &new FileGroupTest(); - $test->run(new HtmlReporter()); - } -?> -</pre> - This approach requires the guard to be set when including - the group test file, but this is still less hassle than - lots of separate runner files. - You include the same guard on the top level tests to make sure - that <span class="new_code">run()</span> will run once only - from the top level script that has been invoked. -<pre> -<?php<strong> - define('RUNNER', true);</strong> - require_once('file_group_test.php'); - - $test = &new BigGroupTest('Big group'); - $test->addTestCase(new FileGroupTest()); - $test->addTestCase(...); - $test->run(new HtmlReporter()); -?> -</pre> - As with the normal test cases, a <span class="new_code">GroupTest</span> can - be loaded with the <span class="new_code">GroupTest::addTestFile()</span> method. -<pre> -<?php - define('RUNNER', true); - - $test = &new BigGroupTest('Big group');<strong> - $test->addTestFile('file_group_test.php'); - $test->addTestFile(...);</strong> - $test->run(new HtmlReporter()); -?> -</pre> - </p> - - <p> -<a class="target" name="legacy"> -<h2>Integrating legacy test cases</h2> -</a> -</p> - <p> - If you already have unit tests for your code or are extending external - classes that have tests, it is unlikely that all of the test cases - are in SimpleTest format. - Fortunately it is possible to incorporate test cases from other - unit testers directly into SimpleTest group tests. - </p> - <p> - Say we have the following - <a href="http://sourceforge.net/projects/phpunit">PhpUnit</a> - test case in the file <em>config_test.php</em>... -<pre> -<strong>class ConfigFileTest extends TestCase { - function ConfigFileTest() { - $this->TestCase('Config file test'); - } - - function testContents() { - $config = new ConfigFile('test.conf'); - $this->assertRegexp('/me/', $config->getValue('username')); - } -}</strong> -</pre> - The group test can recognise this as long as we include - the appropriate adapter class before we add the test - file... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php');<strong> - require_once('simpletest/adapters/phpunit_test_case.php');</strong> - - $test = &new GroupTest('All file tests');<strong> - $test->addTestFile('config_test.php');</strong> - $test->run(new HtmlReporter()); -?> -</pre> - There are only two adapters, the other is for the - <a href="http://pear.php.net/manual/en/package.php.phpunit.php">PEAR</a> - 1.0 unit tester... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php');<strong> - require_once('simpletest/adapters/pear_test_case.php');</strong> - - $test = &new GroupTest('All file tests');<strong> - $test->addTestFile('some_pear_test_cases.php');</strong> - $test->run(new HtmlReporter()); -?> -</pre> - The PEAR test cases can be freely mixed with SimpleTest - ones even in the same test file, - but you cannot use SimpleTest assertions in the legacy - test case versions. - This is done as a check that you are not accidently making - your test cases completely dependent on SimpleTest. - You may want to do a PEAR release of your library for example - which would mean shipping it with valid PEAR::PhpUnit test - cases. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/index.html b/test_tools/simpletest/docs/en/index.html deleted file mode 100755 index 04797272..00000000 --- a/test_tools/simpletest/docs/en/index.html +++ /dev/null @@ -1,467 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title> - Download the Simple Test testing framework - - Unit tests and mock objects for PHP - </title> -<link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> -</head> -<body> -<div class="menu_back"> -<div class="menu"> -<h2> -<span class="chosen">SimpleTest</span> -</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> -<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>Simple Test for PHP</h1> -<div class="content"> - - - <p> - The following assumes that you are familiar with the concept - of unit testing as well as the PHP web development language. - It is a guide for the impatient new user of - <a href="https://sourceforge.net/project/showfiles.php?group_id=76550">SimpleTest</a>. - For fuller documentation, especially if you are new - to unit testing see the ongoing - <a href="unit_test_documentation.html">documentation</a>, and for - example test cases see the - <a href="http://www.lastcraft.com/first_test_tutorial.php">unit testing tutorial</a>. - </p> - - <p> -<a class="target" name="unit"> -<h2>Using the tester quickly</h2> -</a> -</p> - <p> - Amongst software testing tools, a unit tester is the one - closest to the developer. - In the context of agile development the test code sits right - next to the source code as both are written simultaneously. - In this context SimpleTest aims to be a complete PHP developer - test solution and is called "Simple" because it - should be easy to use and extend. - It wasn't a good choice of name really. - It includes all of the typical functions you would expect from - <a href="http://www.junit.org/">JUnit</a> and the - <a href="http://sourceforge.net/projects/phpunit/">PHPUnit</a> - ports, but also adds - <a href="http://www.mockobjects.com">mock objects</a>. - It has some <a href="http://sourceforge.net/projects/jwebunit/">JWebUnit</a> - functionality as well. - This includes web page navigation, cookie testing and form submission. - </p> - <p> - The quickest way to demonstrate is with an example. - </p> - <p> - Let us suppose we are testing a simple file logging class called - <span class="new_code">Log</span> in <em>classes/log.php</em>. - We start by creating a test script which we will call - <em>tests/log_test.php</em> and populate it as follows... -<pre> -<strong><?php -require_once('simpletest/unit_tester.php'); -require_once('simpletest/reporter.php'); -require_once('../classes/log.php'); -?></strong> -</pre> - Here the <em>simpletest</em> folder is either local or in the path. - You would have to edit these locations depending on where you - placed the toolset. - Next we create a test case... -<pre> -<?php -require_once('simpletest/unit_tester.php'); -require_once('simpletest/reporter.php'); -require_once('../classes/log.php'); -<strong> -class TestOfLogging extends UnitTestCase { -}</strong> -?> -</pre> - Now we have five lines of scaffolding code and still no tests. - However from this part on we get return on our investment very quickly. - We'll assume that the <span class="new_code">Log</span> class - takes the file name to write to in the constructor and we have - a temporary folder in which to place this file... -<pre> -<?php -require_once('simpletest/unit_tester.php'); -require_once('simpletest/reporter.php'); -require_once('../classes/log.php'); - -class TestOfLogging extends UnitTestCase { - <strong> - function testCreatingNewFile() { - @unlink('/temp/test.log'); - $log = new Log('/temp/test.log'); - $this->assertFalse(file_exists('/temp/test.log')); - $log->message('Should write this to a file'); - $this->assertTrue(file_exists('/temp/test.log')); - }</strong> -} -?> -</pre> - When a test case runs it will search for any method that - starts with the string <span class="new_code">test</span> - and execute that method. - We would normally have more than one test method of course. - Assertions within the test methods trigger messages to the - test framework which displays the result immediately. - This immediate response is important, not just in the event - of the code causing a crash, but also so that - <span class="new_code">print</span> statements can display - their content right next to the test case concerned. - </p> - <p> - To see these results we have to actually run the tests. - If this is the only test case we wish to run we can achieve - it with... -<pre> -<?php -require_once('simpletest/unit_tester.php'); -require_once('simpletest/reporter.php'); -require_once('../classes/log.php'); - -class TestOfLogging extends UnitTestCase { - - function testCreatingNewFile() { - @unlink('/temp/test.log'); - $log = new Log('/temp/test.log'); - $this->assertFalse(file_exists('/temp/test.log')); - $log->message('Should write this to a file'); - $this->assertTrue(file_exists('/temp/test.log')); - } -} -<strong> -$test = &new TestOfLogging(); -$test->run(new HtmlReporter());</strong> -?> -</pre> - </p> - <p> - On failure the display looks like this... - <div class="demo"> - <h1>testoflogging</h1> - <span class="fail">Fail</span>: testcreatingnewfile->True assertion failed.<br> - <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete. - <strong>1</strong> passes and <strong>1</strong> fails.</div> - </div> - ...and if it passes like this... - <div class="demo"> - <h1>testoflogging</h1> - <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete. - <strong>2</strong> passes and <strong>0</strong> fails.</div> - </div> - And if you get this... - <div class="demo"> - <b>Fatal error</b>: Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b> - </div> - it means you're missing the <em>classes/Log.php</em> file that could look like... -<pre> -<?php -class Log { - - function Log($file_path) { - } - - function message() { - } -} -?>; -</pre> - </p> - - <p> -<a class="target" name="group"> -<h2>Building group tests</h2> -</a> -</p> - <p> - It is unlikely in a real application that we will only ever run - one test case. - This means that we need a way of grouping cases into a test - script that can, if need be, run every test in the application. - </p> - <p> - Our first step is to strip the includes and to undo our - previous hack... -<pre> -<?php<strong> -require_once('../classes/log.php');</strong> - -class TestOfLogging extends UnitTestCase { - - function testCreatingNewFile() { - @unlink('/temp/test.log'); - $log = new Log('/temp/test.log'); - $this->assertFalse(file_exists('/temp/test.log')); - $log->message('Should write this to a file'); - $this->assertTrue(file_exists('/temp/test.log'));<strong> - } -} -?></strong> -</pre> - Next we create a new file called <em>tests/all_tests.php</em> - and insert the following code... -<pre> -<strong><?php -require_once('simpletest/unit_tester.php'); -require_once('simpletest/reporter.php'); - -$test = &new GroupTest('All tests'); -$test->addTestFile('log_test.php'); -$test->run(new HtmlReporter()); -?></strong> -</pre> - The method <span class="new_code">GroupTest::addTestFile()</span> - will include the test case file and read any new classes created - that are descended from <span class="new_code">SimpleTestCase</span>, of which - <span class="new_code">UnitTestCase</span> is one example. - Just the class names are stored for now, so that the test runner - can instantiate the class when it works its way - through your test suite. - </p> - <p> - For this to work properly the test case file should not blindly include - any other test case extensions that do not actually run tests. - This could result in extra test cases being counted during the test - run. - Hardly a major problem, but to avoid this inconvenience simply add - a <span class="new_code">SimpleTestOptions::ignore()</span> directive - somewhere in the test case file. - Also the test case file should not have been included - elsewhere or no cases will be added to this group test. - This would be a more serious error as if the test case classes are - already loaded by PHP the <span class="new_code">GroupTest::addTestFile()</span> - method will not detect them. - </p> - <p> - To display the results it is necessary only to invoke - <em>tests/all_tests.php</em> from the web server. - </p> - - <p> -<a class="target" name="mock"> -<h2>Using mock objects</h2> -</a> -</p> - <p> - Let's move further into the future. - </p> - <p> - Assume that our logging class is tested and completed. - Assume also that we are testing another class that is - required to write log messages, say a - <span class="new_code">SessionPool</span>. - We want to test a method that will probably end up looking - like this... -<pre> -<strong> -class SessionPool { - ... - function logIn($username) { - ... - $this->_log->message("User $username logged in."); - ... - } - ... -} -</strong> -</pre> - In the spirit of reuse we are using our - <span class="new_code">Log</span> class. - A conventional test case might look like this... -<pre> -<strong> -<?php -require_once('../classes/log.php'); -require_once('../classes/session_pool.php'); - -class TestOfSessionLogging extends UnitTestCase { - - function setUp() { - @unlink('/temp/test.log'); - } - - function tearDown() { - @unlink('/temp/test.log'); - } - - function testLogInIsLogged() { - $log = new Log('/temp/test.log'); - $session_pool = &new SessionPool($log); - $session_pool->logIn('fred'); - $messages = file('/temp/test.log'); - $this->assertEqual($messages[0], "User fred logged in.\n"); - } -} -?></strong> -</pre> - This test case design is not all bad, but it could be improved. - We are spending time fiddling with log files which are - not part of our test. Worse, we have created close ties - with the <span class="new_code">Log</span> class and - this test. - What if we don't use files any more, but use ths - <em>syslog</em> library instead? - Did you notice the extra carriage return in the message? - Was that added by the logger? - What if it also added a time stamp or other data? - </p> - <p> - The only part that we really want to test is that a particular - message was sent to the logger. - We reduce coupling if we can pass in a fake logging class - that simply records the message calls for testing, but - takes no action. - It would have to look exactly like our original though. - </p> - <p> - If the fake object doesn't write to a file then we save on deleting - the file before and after each test. We could save even more - test code if the fake object would kindly run the assertion for us. - <p> - </p> - Too good to be true? - Luckily we can create such an object easily... -<pre> -<?php -require_once('../classes/log.php'); -require_once('../classes/session_pool.php');<strong> -Mock::generate('Log');</strong> - -class TestOfSessionLogging extends UnitTestCase { - - function testLogInIsLogged() {<strong> - $log = &new MockLog($this); - $log->expectOnce('message', array('User fred logged in.'));</strong> - $session_pool = &new SessionPool($log); - $session_pool->logIn('fred');<strong> - $log->tally();</strong> - } -} -?> -</pre> - The <span class="new_code">tally()</span> call is needed to - tell the mock object that time is up for the expected call - count. - Without it the mock would wait forever for the method - call to come in without ever actually notifying the test case. - The other test will be triggered when the call to - <span class="new_code">message()</span> is invoked on the - <span class="new_code">MockLog</span> object. - The mock call will trigger a parameter comparison and then send the - resulting pass or fail event to the test display. - Wildcards can be included here too so as to prevent tests - becoming too specific. - </p> - <p> - The mock objects in the SimpleTest suite can have arbitrary - return values set, sequences of returns, return values - selected according to the incoming arguments, sequences of - parameter expectations and limits on the number of times - a method is to be invoked. - </p> - <p> - For this test to run the mock objects library must have been - included in the test suite, say in <em>all_tests.php</em>. - </p> - - <p> -<a class="target" name="web"> -<h2>Web page testing</h2> -</a> -</p> - <p> - One of the requirements of web sites is that they produce web - pages. - If you are building a project top-down and you want to fully - integrate testing along the way then you will want a way of - automatically navigating a site and examining output for - correctness. - This is the job of a web tester. - </p> - <p> - The web testing in SimpleTest is fairly primitive, there is - no JavaScript for example. - To give an idea here is a trivial example where a home - page is fetched, from which we navigate to an "about" - page and then test some client determined content. -<pre> -<?php<strong> -require_once('simpletest/web_tester.php');</strong> -require_once('simpletest/reporter.php'); -<strong> -class TestOfAbout extends WebTestCase { - - function setUp() { - $this->get('http://test-server/index.php'); - $this->clickLink('About'); - } - - function testSearchEngineOptimisations() { - $this->assertTitle('A long title about us for search engines'); - $this->assertWantedPattern('/a popular keyphrase/i'); - } -}</strong> -$test = &new TestOfAbout(); -$test->run(new HtmlReporter()); -?> -</pre> - With this code as an acceptance test you can ensure that - the content always meets the specifications of both the - developers and the other project stakeholders. - </p> - <p> - <a href="http://sourceforge.net/projects/simpletest/"><img src="http://sourceforge.net/sflogo.php?group_id=76550&type=5" width="210" height="62" border="0" alt="SourceForge.net Logo"></a> - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/mock_objects_documentation.html b/test_tools/simpletest/docs/en/mock_objects_documentation.html deleted file mode 100755 index 2f8a1f90..00000000 --- a/test_tools/simpletest/docs/en/mock_objects_documentation.html +++ /dev/null @@ -1,713 +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> -<a href="server_stubs_documentation.html">Server stubs</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 <a href="server_stubs_documentation.html">server stubs</a>. - </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. - 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($this); - } -}</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. - As with stubs we can replace the default null return values... -<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. - As with the stubs we can set wildcards and we can overload the - wildcard parameter. - 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> - All of the patterns available with server stubs are available - to mock objects... -<pre> -class Iterator { - function Iterator() { - } - - function next() { - } -} -</pre> - Again, 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($this); - $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> - A repeat of the stubbed information holder with name/value pairs... -<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($this); -$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> - There are times when you want a specific object to be - dished out by the mock rather than a copy. - Again this is identical to the server stubs mechanism... -<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($this); -$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> -<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($this); - $pool = &new MockSessionPool($this); - $pool->setReturnReference('findSession', $session); - $pool->expectOnce('findSession', array('abc')); - - $log = &new MockLog($this); - $log->expectOnce('message', array('Starting session abc')); - - $logging_pool = &new LoggingSessionPool($pool, $log); - $this->assertReference($logging_pool->findSession('abc'), $session); - $pool->tally(); - $log->tally();</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> - If the call is never made then neither a pass nor a failure will - generated. - To get around this we must tell the mock when the test is over - so that the object can decide if the expectation has been met. - The unit tester assertion for this is triggered by the - <span class="new_code">tally()</span> call at the end of - the test. - </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. - Finally we confirm that the - <span class="new_code">$session</span> we gave our decorator - is the one that we get back and tell the mocks to run their - internal call count tests with the - <span class="new_code">tally()</span> calls. - </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">expectArguments($method, $args)</span></td> - <td style="text-align: center">No</td> - </tr> - <tr> - <td><span class="new_code">expectArgumentsAt($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(&$test, $wildcard = '*') { - $this->BasicMockConnection($test, $wildcard); - $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> - - <p> -<a class="target" name="other_testers"> -<h2>I think SimpleTest stinks!</h2> -</a> -</p> - <p> - But at the time of writing it is the only one with mock objects, - so are you stuck with it? - </p> - <p> - No, not at all. - <a href="simple_test.html">SimpleTest</a> is a toolkit and one of those - tools is the mock objects which can be employed independently. - Suppose you have your own favourite unit tester and all your current - test cases are written using it. - Pretend that you have called your unit tester PHPUnit (everyone else has) - and the core test class looks like this... -<pre> -class PHPUnit { - function PHPUnit() { - } - - function assertion($message, $assertion) { - } - ... -} -</pre> - All the <span class="new_code">assertion()</span> method does - is print some fancy output and the boolean assertion parameter determines - whether to print a pass or a failure. - Let's say that it is used like this... -<pre> -$unit_test = new PHPUnit(); -$unit_test>assertion('I hope this file exists', file_exists('my_file')); -</pre> - How do you use mocks with this? - </p> - <p> - There is a protected method on the base mock class - <span class="new_code">SimpleMock</span> called - <span class="new_code">_assertTrue()</span> and - by overriding this method we can use our own assertion format. - We start with a subclass, in say <em>my_mock.php</em>... -<pre> -<strong><?php - require_once('simpletest/mock_objects.php'); - - class MyMock extends SimpleMock() { - function MyMock(&$test, $wildcard) { - $this->SimpleMock($test, $wildcard); - } - - function _assertTrue($assertion, $message) { - $test = &$this->getTest(); - $test->assertion($message, $assertion); - } - } -?></strong> -</pre> - Now instantiating <span class="new_code">MyMock</span> will create - an object that speaks the same language as your tester. - The catch is of course that we never create such an object, the - code generator does. - We need just one more line of code to tell the generator to use - your mock instead... -<pre> -<?php - require_once('simpletst/mock_objects.php'); - - class MyMock extends SimpleMock() { - function MyMock($test, $wildcard) { - $this->SimpleMock(&$test, $wildcard); - } - - function _assertTrue($assertion, $message , &$test) { - $test->assertion($message, $assertion); - } - }<strong> - SimpleTestOptions::setMockBaseClass('MyMock');</strong> -?> -</pre> - From now on you just include <em>my_mock.php</em> instead of the - default <em>mock_objects.php</em> version and you can introduce - mock objects into your existing test suite. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/overview.html b/test_tools/simpletest/docs/en/overview.html deleted file mode 100755 index d4965de3..00000000 --- a/test_tools/simpletest/docs/en/overview.html +++ /dev/null @@ -1,422 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title> - Overview and feature list for the SimpleTest PHP unit tester and web tester - </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> -<span class="chosen">Overview</span> -</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> -<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>Overview of SimpleTest</h1> -<div class="content"> - <p> -<a class="target" name="summary"> -<h2>What is SimpleTest?</h2> -</a> -</p> - <p> - The heart of SimpleTest is a testing framework built around - test case classes. - These are written as extensions of base test case classes, - each extended with methods that actually contain test code. - Top level test scripts then invoke the <span class="new_code">run()</span> - methods on every one of these test cases in order. - Each test method is written to invoke various assertions that - the developer expects to be true such as - <span class="new_code">assertEqual()</span>. - If the expectation is correct, then a successful result is dispatched to the - observing test reporter, but any failure triggers an alert - and a description of the mismatch. - </p> - <p> - A <a href="unit_test_documentation.html">test case</a> looks like this... -<pre> -<?php -class <strong>MyTestCase</strong> extends UnitTestCase { - <strong> - function testLog() { - $log = &new Log('my.log'); - $log->message('Hello'); - $this->assertTrue(file_exists('my.log')); - }</strong> -} -?> -</pre> - </p> - <p> - These tools are designed for the developer. - Tests are written in the PHP language itself more or less - as the application itself is built. - The advantage of using PHP itself as the testing language is that - there are no new languages to learn, testing can start straight away, - and the developer can test any part of the code. - Basically, all parts that can be accessed by the application code can also be - accessed by the test code if they are in the same language. - </p> - <p> - The simplest type of test case is the - <a href="unit_tester_documentation.html">UnitTestCase</a>. - This class of test case includes standard tests for equality, - references and pattern matching. - All these test the typical expectations of what you would - expect the result of a function or method to be. - This is by far the most common type of test in the daily - routine of development, making up about 95% of test cases. - </p> - <p> - The top level task of a web application though is not to - produce correct output from its methods and objects, but - to generate web pages. - The <a href="web_tester_documentation.html">WebTestCase</a> class tests web - pages. - It simulates a web browser requesting a page, complete with - cookies, proxies, secure connections, authentication, forms, frames and most - navigation elements. - With this type of test case, the developer can assert that - information is present in the page and that forms and - sessions are handled correctly. - </p> - <p> - A <a href="web_tester_documentation.html">WebTestCase</a> looks like this... -<pre> -<?php -class <strong>MySiteTest</strong> extends WebTestCase { - <strong> - function testHomePage() { - $this->get('http://www.my-site.com/index.php'); - $this->assertTitle('My Home Page'); - $this->clickLink('Contact'); - $this->assertTitle('Contact me'); - $this->assertWantedPattern('/Email me at/'); - }</strong> -} -?> -</pre> - </p> - - <p> -<a class="target" name="features"> -<h2>Feature list</h2> -</a> -</p> - <p> - The following is a very rough outline of past and future features - and their expected point of release. - I am afraid it is liable to change without warning as meeting the - milestones rather depends on time available. - Green stuff has been coded, but not necessarily released yet. - If you have a pressing need for a green but unreleased feature - then you should check-out the code from sourceforge CVS directly. - A released feature is marked as "Done". - <table> -<thead> - <tr> -<th>Feature</th><th>Description</th><th>Release</th> -</tr> - </thead> -<tbody> -<tr> - <td>Unit test case</td> - <td>Core test case class and assertions</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Html display</td> - <td>Simplest possible display</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Autoloading of test cases</td> - <td> - Reading a file with test cases and loading them into a - group test automatically - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Mock objects code generator</td> - <td> - Objects capable of simulating other objects removing - test dependencies - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Server stubs</td> - <td> - Mocks without expectations to be used outside of test cases, - e.g. for prototyping - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Integration of other unit testers</td> - <td> - The ability to read and simulate test cases from PHPUnit - and PEAR::PhpUnit - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Web test case</td> - <td>Basic pattern matching of fetched pages</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>HTML parsing of pages</td> - <td>Allows link following and title tag matching</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Partial mocks</td> - <td> - Mocking parts of a class for testing less than a class - or for complex simulations - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Web cookie handling</td> - <td>Correct handling of cookies when fetching pages</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Following redirects</td> - <td>Page fetching automatically follows 300 redirects</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Form parsing</td> - <td>Ability to submit simple forms and read default form values</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Command line interface</td> - <td>Test display without the need of a web browser</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Exposure of expectation classes</td> - <td>Can create precise tests with mocks as well as test cases</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>XML output and parsing</td> - <td> - Allows multi host testing and the integration of acceptance - testing extensions - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Command line test case</td> - <td>Allows testing of utilities and file handling</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>PHP Documentor compatibility</td> - <td>Fully generated class level documentation</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Browser interface</td> - <td> - Exposure of lower level web browser interface for more - detailed test cases - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>HTTP authentication</td> - <td> - Fetching protected web pages with basic authentication - only - </td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Browser navigation buttons</td> - <td>Back, forward and retry</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>SSL support</td> - <td>Can connect to https: pages</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Proxy support</td> - <td>Can connect via. common proxies</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Frames support</td> - <td>Handling of frames in web test cases</td> - <td style="color: green;">Done</td> - </tr> - <tr> - <td>Improved display</td> - <td>Better web GUI with tree display of test cases</td> - <td style="color: red;">1.1</td> - </tr> - <tr> - <td>Localisation</td> - <td>Messages abstracted and code generated from XML</td> - <td style="color: red;">1.1</td> - </tr> - <tr> - <td>File upload testing</td> - <td>Can simulate the input type file tag</td> - <td style="color: red;">1.1</td> - </tr> - <tr> - <td>Mocking interfaces</td> - <td>Can generate mock objects to interfaces as well as classes</td> - <td style="color: red;">2.0</td> - </tr> - <tr> - <td>Testing exceptions</td> - <td>Similar to testing PHP errors</td> - <td style="color: red;">2.0</td> - </tr> - <tr> - <td>XPath searching of elements</td> - <td>Can make use of HTML tidy for faster and more flexible content matching</td> - <td style="color: red;">2.0</td> - </tr> - </tbody> -</table> - PHP5 migraton will start straight after the version 1.1 series, - whereupon PHP4 will no longer be supported. - SimpleTest is currently compatible with PHP5, but will not - make use of all of the new features until version 2. - </p> - - <p> -<a class="target" name="resources"> -<h2>Web resources for testing</h2> -</a> -</p> - <p> - Process is at least as important as tools. - The type of process that makes the heaviest use of a developer's - testing tool is of course - <a href="http://www.extremeprogramming.org/">Extreme Programming</a>. - This is one of the - <a href="http://www.agilealliance.com/articles/index">Agile Methodologies</a> - which combine various practices to "flatten the cost curve" of software development. - More extreme still is <a href="http://www.testdriven.com/modules/news/">Test Driven Development</a>, - where you very strictly adhere to the rule of no coding until you have a test. - If you're more of a planner or believe that experience trumps evolution, - you may prefer the - <a href="http://www.therationaledge.com/content/dec_01/f_spiritOfTheRUP_pk.html">RUP</a> approach. - I haven't tried it, but even I can see that you will need test tools (see figure 9). - </p> - <p> - Most unit testers clone <a href="http://www.junit.org/">JUnit</a> to some degree, - as far as the interface at least. There is a wealth of information on the - JUnit site including the - <a href="http://junit.sourceforge.net/doc/faq/faq.htm">FAQ</a> - which contains plenty of general advice on testing. - Once you get bitten by the bug you will certainly appreciate the phrase - <a href="http://junit.sourceforge.net/doc/testinfected/testing.htm">test infected</a> - coined by Eric Gamma. - If you are still reviewing which unit tester to use the main choices - are <a href="http://phpunit.sourceforge.net/">PHPUnit</a> - and <a href="http://pear.php.net/manual/en/package.php.phpunit.php">Pear PHP::PHPUnit</a>. - They currently lack a lot of features found in - <a href="http://www.lastcraft.com/simple_test.php">SimpleTest</a>, but the PEAR - version at least has been upgraded for PHP5 and is recommended if you are porting - existing <a href="http://www.junit.org/">JUnit</a> test cases. - </p> - <p> - Library writers don't seem to ship tests with their code very often - which is a shame. - Library code that includes tests can be more safely refactored and - the test code can act as additional documentation in a fairly standard - form. - This can save trawling the source code for clues when problems occour, - especially when upgrading such a library. - Libraries using SimpleTest for their unit testing include - <a href="http://wact.sourceforge.net/">WACT</a> and - <a href="http://sourceforge.net/projects/htmlsax">PEAR::XML_HTMLSax</a>. - </p> - <p> - There is currently a sad lack of material on mock objects, which is a shame - as unit testing without them is a lot more work. - The <a href="http://www.sidewize.com/company/mockobjects.pdf">original mock objects paper</a> - is very Java focused, but still worth a read. - As a new technology there are plenty of discussions and debate on how to use mocks, - often on Wikis such as - <a href="http://xpdeveloper.com/cgi-bin/oldwiki.cgi?MockObjects">Extreme Tuesday</a> - or <a href="http://www.mockobjects.com/wiki/MocksObjectsPaper">www.mockobjects.com</a> - or <a href="http://c2.com/cgi/wiki?MockObject">the original C2 Wiki</a>. - Injecting mocks into a class is the main area of debate for which this - <a href="http://www-106.ibm.com/developerworks/java/library/j-mocktest.html">paper on IBM</a> - makes a good starting point. - </p> - <p> - There are plenty of web testing tools, but most are written in Java and - tutorials and advice are rather thin on the ground. - The only hope is to look at the documentation for - <a href="http://httpunit.sourceforge.net/">HTTPUnit</a>, - <a href="http://htmlunit.sourceforge.net/">HTMLUnit</a> - or <a href="http://jwebunit.sourceforge.net/">JWebUnit</a> and hope for clues. - There are some XML driven test frameworks, but again most - require Java to run. - As SimpleTest does not support JavaScript you would probably - have to look at these tools anyway if you have highly dynamic - pages. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> 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> diff --git a/test_tools/simpletest/docs/en/reporter_documentation.html b/test_tools/simpletest/docs/en/reporter_documentation.html deleted file mode 100755 index 44be8b1e..00000000 --- a/test_tools/simpletest/docs/en/reporter_documentation.html +++ /dev/null @@ -1,515 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest for PHP test runner and display 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> -<a href="partial_mocks_documentation.html">Partial mocks</a> -</li> -<li> -<span class="chosen">Reporting</span> -</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>Test reporter documentation</h1> -<div class="content"> - - <p> - SimpleTest pretty much follows the MVC pattern - (Model-View-Controller). - The reporter classes are the view and the model is your - test cases and their hiearchy. - The controller is mostly hidden from the user of - SimpleTest unless you want to change how the test cases - are actually run, in which case it is possible to - override the runner objects from within the test case. - As usual with MVC, the controller is mostly undefined - and there are other places to control the test run. - </p> - - <p> -<a class="target" name="html"> -<h2>Reporting results in HTML</h2> -</a> -</p> - <p> - The default test display is minimal in the extreme. - It reports success and failure with the conventional red and - green bars and shows a breadcrumb trail of test groups - for every failed assertion. - Here's a fail... - <div class="demo"> - <h1>File test</h1> - <span class="fail">Fail</span>: createnewfile->True assertion failed.<br> - <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete. - <strong>0</strong> passes, <strong>1</strong> fails and <strong>0</strong> exceptions.</div> - </div> - And here all tests passed... - <div class="demo"> - <h1>File test</h1> - <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete. - <strong>1</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div> - </div> - The good news is that there are several points in the display - hiearchy for subclassing. - </p> - <p> - For web page based displays there is the - <span class="new_code">HtmlReporter</span> class with the following - signature... -<pre> -class HtmlReporter extends SimpleReporter { - public HtmlReporter($encoding) { ... } - public makeDry(boolean $is_dry) { ... } - public void paintHeader(string $test_name) { ... } - public void sendNoCacheHeaders() { ... } - public void paintFooter(string $test_name) { ... } - public void paintGroupStart(string $test_name, integer $size) { ... } - public void paintGroupEnd(string $test_name) { ... } - public void paintCaseStart(string $test_name) { ... } - public void paintCaseEnd(string $test_name) { ... } - public void paintMethodStart(string $test_name) { ... } - public void paintMethodEnd(string $test_name) { ... } - public void paintFail(string $message) { ... } - public void paintPass(string $message) { ... } - public void paintError(string $message) { ... } - public void paintException(string $message) { ... } - public void paintMessage(string $message) { ... } - public void paintFormattedMessage(string $message) { ... } - protected string _getCss() { ... } - public array getTestList() { ... } - public integer getPassCount() { ... } - public integer getFailCount() { ... } - public integer getExceptionCount() { ... } - public integer getTestCaseCount() { ... } - public integer getTestCaseProgress() { ... } -} -</pre> - Here is what some of these methods mean. First the display methods - that you will probably want to override... - <ul class="api"> - <li> - <span class="new_code">HtmlReporter(string $encoding)</span> -<br> - is the constructor. - Note that the unit test sets up the link to the display - rather than the other way around. - The display is a mostly passive receiver of test events. - This allows easy adaption of the display for other test - systems beside unit tests, such as monitoring servers. - The encoding is the character encoding you wish to - display the test output in. - In order to correctly render debug output when - using the web tester, this should match the encoding - of the site you are trying to test. - The available character set strings are described in - the PHP <a href="http://www.php.net/manual/en/function.htmlentities.php">html_entities()</a> - function. - </li> - <li> - <span class="new_code">void paintHeader(string $test_name)</span> -<br> - is called once at the very start of the test when the first - start event arrives. - The first start event is usually delivered by the top level group - test and so this is where <span class="new_code">$test_name</span> - comes from. - It paints the page titles, CSS, body tag, etc. - It returns nothing (<span class="new_code">void</span>). - </li> - <li> - <span class="new_code">void paintFooter(string $test_name)</span> -<br> - Called at the very end of the test to close any tags opened - by the page header. - By default it also displays the red/green bar and the final - count of results. - Actually the end of the test happens when a test end event - comes in with the same name as the one that started it all - at the same level. - The tests nest you see. - Closing the last test finishes the display. - </li> - <li> - <span class="new_code">void paintMethodStart(string $test_name)</span> -<br> - is called at the start of each test method. - The name normally comes from method name. - The other test start events behave the same way except - that the group test one tells the reporter how large - it is in number of held test cases. - This is so that the reporter can display a progress bar - as the runner churns through the test cases. - </li> - <li> - <span class="new_code">void paintMethodEnd(string $test_name)</span> -<br> - backs out of the test started with the same name. - </li> - <li> - <span class="new_code">void paintFail(string $message)</span> -<br> - paints a failure. - By default it just displays the word fail, a breadcrumbs trail - showing the current test nesting and the message issued by - the assertion. - </li> - <li> - <span class="new_code">void paintPass(string $message)</span> -<br> - by default does nothing. - </li> - <li> - <span class="new_code">string _getCss()</span> -<br> - Returns the CSS styles as a string for the page header - method. - Additional styles have to be appended here if you are - not overriding the page header. - You will want to use this method in an overriden page header - if you want to include the original CSS. - </li> - </ul> - There are also some accessors to get information on the current - state of the test suite. - Use these to enrich the display... - <ul class="api"> - <li> - <span class="new_code">array getTestList()</span> -<br> - is the first convenience method for subclasses. - Lists the current nesting of the tests as a list - of test names. - The first, most deeply nested test, is first in the - list and the current test method will be last. - </li> - <li> - <span class="new_code">integer getPassCount()</span> -<br> - returns the number of passes chalked up so far. - Needed for the display at the end. - </li> - <li> - <span class="new_code">integer getFailCount()</span> -<br> - is likewise the number of fails so far. - </li> - <li> - <span class="new_code">integer getExceptionCount()</span> -<br> - is likewise the number of errors so far. - </li> - <li> - <span class="new_code">integer getTestCaseCount()</span> -<br> - is the total number of test cases in the test run. - This includes the grouping tests themselves. - </li> - <li> - <span class="new_code">integer getTestCaseProgress()</span> -<br> - is the number of test cases completed so far. - </li> - </ul> - One simple modification is to get the HtmlReporter to display - the passes as well as the failures and errors... -<pre> -<strong>class ShowPasses extends HtmlReporter { - - function paintPass($message) { - parent::paintPass($message); - print "&<span class=\"pass\">Pass</span>: "; - $breadcrumb = $this->getTestList(); - array_shift($breadcrumb); - print implode("-&gt;", $breadcrumb); - print "-&gt;$message<br />\n"; - } - - function _getCss() { - return parent::_getCss() . ' .pass { color: green; }'; - } -}</strong> -</pre> - </p> - <p> - One method that was glossed over was the <span class="new_code">makeDry()</span> - method. - If you run this method, with no parameters, on the reporter - before the test suite is run no actual test methods - will be called. - You will still get the events of entering and leaving the - test methods and test cases, but no passes or failures etc, - because the test code will not actually be executed. - </p> - <p> - The reason for this is to allow for more sophistcated - GUI displays that allow the selection of individual test - cases. - In order to build a list of possible tests they need a - report on the test structure for drawing, say a tree view - of the test suite. - With a reporter set to dry run that just sends drawing events - this is easily accomplished. - </p> - - <p> -<a class="target" name="other"> -<h2>Extending the reporter</h2> -</a> -</p> - <p> - Rather than simply modifying the existing display, you might want to - produce a whole new HTML look, or even generate text or XML. - Rather than override every method in - <span class="new_code">HtmlReporter</span> we can take one - step up the class hiearchy to <span class="new_code">SimpleReporter</span> - in the <em>simple_test.php</em> source file. - </p> - <p> - A do nothing display, a blank canvas for your own creation, would - be... -<pre> -<strong>require_once('simpletest/simple_test.php');</strong> - -class MyDisplay extends SimpleReporter {<strong> - </strong> - function paintHeader($test_name) { - } - - function paintFooter($test_name) { - } - - function paintStart($test_name, $size) {<strong> - parent::paintStart($test_name, $size);</strong> - } - - function paintEnd($test_name, $size) {<strong> - parent::paintEnd($test_name, $size);</strong> - } - - function paintPass($message) {<strong> - parent::paintPass($message);</strong> - } - - function paintFail($message) {<strong> - parent::paintFail($message);</strong> - } -} -</pre> - No output would come from this class until you add it. - </p> - - <p> -<a class="target" name="cli"> -<h2>The command line reporter</h2> -</a> -</p> - <p> - SimpleTest also ships with a minimal command line reporter. - The interface mimics JUnit to some extent, but paints the - failure messages as they arrive. - To use the command line reporter simply substitute it - for the HTML version... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - - $test = &new GroupTest('File test'); - $test->addTestFile('tests/file_test.php'); - $test->run(<strong>new TextReporter()</strong>); -?> -</pre> - Then invoke the test suite from the command line... -<pre class="shell"> -php file_test.php -</pre> - You will need the command line version of PHP installed - of course. - A passing test suite looks like this... -<pre class="shell"> -File test -OK -Test cases run: 1/1, Failures: 0, Exceptions: 0 -</pre> - A failure triggers a display like this... -<pre class="shell"> -File test -1) True assertion failed. - in createnewfile -FAILURES!!! -Test cases run: 1/1, Failures: 1, Exceptions: 0 -</pre> - </p> - <p> - One of the main reasons for using a command line driven - test suite is of using the tester as part of some automated - process. - To function properly in shell scripts the test script should - return a non-zero exit code on failure. - If a test suite fails the value <span class="new_code">false</span> - is returned from the <span class="new_code">SimpleTest::run()</span> - method. - We can use that result to exit the script with the desired return - code... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - - $test = &new GroupTest('File test'); - $test->addTestFile('tests/file_test.php'); - <strong>exit ($test->run(new TextReporter()) ? 0 : 1);</strong> -?> -</pre> - Of course we don't really want to create two test scripts, - a command line one and a web browser one, for each test suite. - The command line reporter includes a method to sniff out the - run time environment... -<pre> -<?php - require_once('simpletest/unit_tester.php'); - require_once('simpletest/reporter.php'); - - $test = &new GroupTest('File test'); - $test->addTestFile('tests/file_test.php'); - <strong>if (TextReporter::inCli()) {</strong> - exit ($test->run(new TextReporter()) ? 0 : 1); - <strong>}</strong> - $test->run(new HtmlReporter()); -?> -</pre> - This is the form used within SimpleTest itself. - </p> - - <p> -<a class="target" name="xml"> -<h2>Remote testing</h2> -</a> -</p> - <p> - SimpleTest ships with an <span class="new_code">XmlReporter</span> class - used for internal communication. - When run the output looks like... -<pre class="shell"> -<?xml version="1.0"?> -<run> - <group size="4"> - <name>Remote tests</name> - <group size="4"> - <name>Visual test with 48 passes, 48 fails and 4 exceptions</name> - <case> - <name>testofunittestcaseoutput</name> - <test> - <name>testofresults</name> - <pass>This assertion passed</pass> - <fail>This assertion failed</fail> - </test> - <test> - ... - </test> - </case> - </group> - </group> -</run> -</pre> - You can make use of this format with the parser - supplied as part of SimpleTest itself. - This is called <span class="new_code">SimpleTestXmlParser</span> and - resides in <em>xml.php</em> within the SimpleTest package... -<pre> -<?php - require_once('simpletest/xml.php'); - - ... - $parser = &new SimpleTestXmlParser(new HtmlReporter()); - $parser->parse($test_output); -?> -</pre> - The <span class="new_code">$test_output</span> should be the XML format - from the XML reporter, and could come from say a command - line run of a test case. - The parser sends events to the reporter just like any - other test run. - There are some odd occasions where this is actually useful. - </p> - <p> - A problem with large test suites is thet they can exhaust - the default 8Mb memory limit on a PHP process. - By having the test groups output in XML and run in - separate processes, the output can be reparsed to - aggregate the results into a much smaller footprint top level - test. - </p> - <p> - Because the XML output can come from anywhere, this opens - up the possibility of aggregating test runs from remote - servers. - A test case already exists to do this within the SimpleTest - framework, but it is currently experimental... -<pre> -<?php - <strong>require_once('../remote.php');</strong> - require_once('../reporter.php'); - - $test_url = ...; - $dry_url = ...; - - $test = &new GroupTest('Remote tests'); - $test->addTestCase(<strong>new RemoteTestCase($test_url, $dry_url)</strong>); - $test->run(new HtmlReporter()); -?> -</pre> - The <span class="new_code">RemoteTestCase</span> takes the actual location - of the test runner, basically a web page in XML format. - It also takes the URL of a reporter set to do a dry run. - This is so that progress can be reported upward correctly. - The <span class="new_code">RemoteTestCase</span> can be added to test suites - just like any other group test. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/server_stubs_documentation.html b/test_tools/simpletest/docs/en/server_stubs_documentation.html deleted file mode 100755 index 4b18bb0b..00000000 --- a/test_tools/simpletest/docs/en/server_stubs_documentation.html +++ /dev/null @@ -1,388 +0,0 @@ -<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> diff --git a/test_tools/simpletest/docs/en/unit_test_documentation.html b/test_tools/simpletest/docs/en/unit_test_documentation.html deleted file mode 100755 index 6aa8d8a7..00000000 --- a/test_tools/simpletest/docs/en/unit_test_documentation.html +++ /dev/null @@ -1,387 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>SimpleTest for PHP regression test 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> -<span class="chosen">Unit tester</span> -</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> -<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>PHP Unit Test documentation</h1> -<div class="content"> - <p> -<a class="target" name="unit"> -<h2>Unit test cases</h2> -</a> -</p> - <p> - The core system is a regression testing framework built around - test cases. - A sample test case looks like this... -<pre> -<strong>class FileTestCase extends UnitTestCase { -}</strong> -</pre> - If no test name is supplied when chaining the constructor then - the class name will be taken instead. - This will be the name displayed in the test results. - </p> - <p> - Actual tests are added as methods in the test case whose names - by default start with the string "test" and - when the test case is invoked all such methods are run in - the order that PHP introspection finds them. - As many test methods can be added as needed. - For example... -<pre> -require_once('../classes/writer.php'); - -class FileTestCase extends UnitTestCase { - function FileTestCase() { - $this->UnitTestCase('File test'); - }<strong> - - function setUp() { - @unlink('../temp/test.txt'); - } - - function tearDown() { - @unlink('../temp/test.txt'); - } - - function testCreation() { - $writer = &new FileWriter('../temp/test.txt'); - $writer->write('Hello'); - $this->assertTrue(file_exists('../temp/test.txt'), 'File created'); - }</strong> -} -</pre> - The constructor is optional and usually omitted. - Without a name, the class name is taken as the name of the test case. - </p> - <p> - Our only test method at the moment is <span class="new_code">testCreation()</span> - where we check that a file has been created by our - <span class="new_code">Writer</span> object. - We could have put the <span class="new_code">unlink()</span> - code into this method as well, but by placing it in - <span class="new_code">setUp()</span> and - <span class="new_code">tearDown()</span> we can use it with - other test methods that we add. - </p> - <p> - The <span class="new_code">setUp()</span> method is run - just before each and every test method. - <span class="new_code">tearDown()</span> is run just after - each and every test method. - </p> - <p> - You can place some test case set up into the constructor to - be run once for all the methods in the test case, but - you risk test inteference that way. - This way is slightly slower, but it is safer. - Note that if you come from a JUnit background this will not - be the behaviour you are used to. - JUnit surprisingly reinstantiates the test case for each test - method to prevent such interference. - SimpleTest requires the end user to use <span class="new_code">setUp()</span>, but - supplies additional hooks for library writers. - </p> - <p> - The means of reporting test results (see below) are by a - visiting display class - that is notified by various <span class="new_code">assert...()</span> - methods. - Here is the full list for the <span class="new_code">UnitTestCase</span> - class, the default for SimpleTest... - <table> -<tbody> - <tr> -<td><span class="new_code">assertTrue($x)</span></td><td>Fail if $x is false</td> -</tr> - <tr> -<td><span class="new_code">assertFalse($x)</span></td><td>Fail if $x is true</td> -</tr> - <tr> -<td><span class="new_code">assertNull($x)</span></td><td>Fail if $x is set</td> -</tr> - <tr> -<td><span class="new_code">assertNotNull($x)</span></td><td>Fail if $x not set</td> -</tr> - <tr> -<td><span class="new_code">assertIsA($x, $t)</span></td><td>Fail if $x is not the class or type $t</td> -</tr> - <tr> -<td><span class="new_code">assertNotA($x, $t)</span></td><td>Fail if $x is of the class or type $t</td> -</tr> - <tr> -<td><span class="new_code">assertEqual($x, $y)</span></td><td>Fail if $x == $y is false</td> -</tr> - <tr> -<td><span class="new_code">assertNotEqual($x, $y)</span></td><td>Fail if $x == $y is true</td> -</tr> - <tr> -<td><span class="new_code">assertIdentical($x, $y)</span></td><td>Fail if $x == $y is false or a type mismatch</td> -</tr> - <tr> -<td><span class="new_code">assertNotIdentical($x, $y)</span></td><td>Fail if $x == $y is true and types match</td> -</tr> - <tr> -<td><span class="new_code">assertReference($x, $y)</span></td><td>Fail unless $x and $y are the same variable</td> -</tr> - <tr> -<td><span class="new_code">assertCopy($x, $y)</span></td><td>Fail if $x and $y are the same variable</td> -</tr> - <tr> -<td><span class="new_code">assertWantedPattern($p, $x)</span></td><td>Fail unless the regex $p matches $x</td> -</tr> - <tr> -<td><span class="new_code">assertNoUnwantedPattern($p, $x)</span></td><td>Fail if the regex $p matches $x</td> -</tr> - <tr> -<td><span class="new_code">assertNoErrors()</span></td><td>Fail if any PHP error occoured</td> -</tr> - <tr> -<td><span class="new_code">assertError($x)</span></td><td>Fail if no PHP error or incorrect message</td> -</tr> - <tr> -<td><span class="new_code">assertErrorPattern($p)</span></td><td>Fail unless the error matches the regex $p</td> -</tr> - </tbody> -</table> - All assertion methods can take an optional description to - label the displayed result with. - If omitted a default message is sent instead which is usually - sufficient. - This default message can still be embedded in your own message - if you include "%s" within the string. - All the assertions return true on a pass or false on failure. - </p> - <p> - Some examples... -<pre> -<strong>$variable = null; -$this->assertNull($variable, 'Should be cleared');</strong> -</pre> - ...will pass and normally show no message. - If you have - <a href="http://www.lastcraft.com/display_subclass_tutorial.php">set up the tester to display passes</a> - as well then the message will be displayed as is. -<pre> -<strong>$this->assertIdentical(0, false, 'Zero is not false [%s]');</strong> -</pre> - This will fail as it performs a type - check as well as a comparison between the two values. - The "%s" part is replaced by the default - error message that would have been shown if we had not - supplied our own. - This also allows us to nest test messages. -<pre> -<strong>$a = 1; -$b = $a; -$this->assertReference($a, $b);</strong> -</pre> - Will fail as the variable <span class="new_code">$a</span> is a copy of <span class="new_code">$b</span>. -<pre> -<strong>$this->assertWantedPattern('/hello/i', 'Hello world');</strong> -</pre> - This will pass as using a case insensitive match the string - <span class="new_code">hello</span> is contained in <span class="new_code">Hello world</span>. -<pre> -<strong>trigger_error('Disaster'); -trigger_error('Catastrophe'); -$this->assertError(); -$this->assertError('Catastrophe'); -$this->assertNoErrors();</strong> -</pre> - This one takes some explanation as in fact they all pass! - </p> - <p> - PHP errors in SimpleTest are trapped and placed in a queue. - Here the first error check catches the "Disaster" - message without checking the text and passes. - This removes the error from the queue. - The next error check tests not only the existence of the error, - but also the text which here matches so another pass. - With the queue now empty the last test will pass as well. - If any unchecked errors are left at the end of a test method then - an exception will be reported in the test. - Note that SimpleTest cannot catch compile time PHP errors. - </p> - <p> - The test cases also have some convenience methods for debugging - code or extending the suite... - <table> -<tbody> - <tr> -<td><span class="new_code">setUp()</span></td><td>Runs this before each test method</td> -</tr> - <tr> -<td><span class="new_code">tearDown()</span></td><td>Runs this after each test method</td> -</tr> - <tr> -<td><span class="new_code">pass()</span></td><td>Sends a test pass</td> -</tr> - <tr> -<td><span class="new_code">fail()</span></td><td>Sends a test failure</td> -</tr> - <tr> -<td><span class="new_code">error()</span></td><td>Sends an exception event</td> -</tr> - <tr> -<td><span class="new_code">sendMessage()</span></td><td>Sends a status message to those displays that support it</td> -</tr> - <tr> -<td><span class="new_code">signal($type, $payload)</span></td><td>Sends a user defined message to the test reporter</td> -</tr> - <tr> -<td><span class="new_code">dump($var)</span></td><td>Does a formatted <span class="new_code">print_r()</span> for quick and dirty debugging</td> -</tr> - <tr> -<td><span class="new_code">swallowErrors()</span></td><td>Clears the error queue</td> -</tr> - </tbody> -</table> - </p> - - <p> -<a class="target" name="extending_unit"> -<h2>Extending test cases</h2> -</a> -</p> - <p> - Of course additional test methods can be added to create - specific types of test case too so as to extend framework... -<pre> -require_once('simpletest/unit_tester.php'); -<strong> -class FileTester extends UnitTestCase { - function FileTester($name = false) { - $this->UnitTestCase($name); - } - - function assertFileExists($filename, $message = '%s') { - $this->assertTrue( - file_exists($filename), - sprintf($message, 'File [$filename] existence check')); - }</strong> -} -</pre> - Here the SimpleTest library is held in a folder called - <em>simpletest</em> that is local. - Substitute your own path for this. - </p> - <p> - This new case can be now be inherited just like - a normal test case... -<pre> -class FileTestCase extends <strong>FileTester</strong> { - - function setUp() { - @unlink('../temp/test.txt'); - } - - function tearDown() { - @unlink('../temp/test.txt'); - } - - function testCreation() { - $writer = &new FileWriter('../temp/test.txt'); - $writer->write('Hello');<strong> - $this->assertFileExists('../temp/test.txt');</strong> - } -} -</pre> - </p> - <p> - If you want a test case that does not have all of the - <span class="new_code">UnitTestCase</span> assertions, - only your own and <span class="new_code">assertTrue()</span>, - you need to extend the <span class="new_code">SimpleTestCase</span> - class instead. - It is found in <em>simple_test.php</em> rather than - <em>unit_tester.php</em>. - See <a href="group_test_documentation.html">later</a> if you - want to incorporate other unit tester's - test cases in your test suites. - </p> - - <p> -<a class="target" name="running_unit"> -<h2>Running a single test case</h2> -</a> -</p> - <p> - You won't often run single test cases except when bashing - away at a module that is having difficulty and you don't - want to upset the main test suite. - Here is the scaffolding needed to run the a lone test case... -<pre> -<?php - require_once('simpletest/unit_tester.php');<strong> - require_once('simpletest/reporter.php');</strong> - require_once('../classes/writer.php'); - - class FileTestCase extends UnitTestCase { - function FileTestCase() { - $this->UnitTestCase('File test'); - } - }<strong> - - $test = &new FileTestCase(); - $test->run(new HtmlReporter());</strong> -?> -</pre> - This script will run as is, but will output zero passes - and zero failures until test methods are added. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> diff --git a/test_tools/simpletest/docs/en/web_tester_documentation.html b/test_tools/simpletest/docs/en/web_tester_documentation.html deleted file mode 100755 index 51f604be..00000000 --- a/test_tools/simpletest/docs/en/web_tester_documentation.html +++ /dev/null @@ -1,508 +0,0 @@ -<html> -<head> -<META http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<title>Simple Test for PHP web script testing 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> -<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> -<span class="chosen">Web tester</span> -</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>Web tester documentation</h1> -<div class="content"> - <p> -<a class="target" name="fetch"> -<h2>Fetching a page</h2> -</a> -</p> - <p> - Testing classes is all very well, but PHP is predominately - a language for creating functionality within web pages. - How do we test the front end presentation role of our PHP - applications? - Well the web pages are just text, so we should be able to - examine them just like any other test data. - </p> - <p> - This leads to a tricky issue. - If we test at too low a level, testing for matching tags - in the page with pattern matching for example, our tests will - be brittle. - The slightest change in layout could break a large number of - tests. - If we test at too high a level, say using mock versions of a - template engine, then we lose the ability to automate some classes - of test. - For example, the interaction of forms and navigation will - have to be tested manually. - These types of test are extremely repetitive and error prone. - </p> - <p> - SimpleTest includes a special form of test case for the testing - of web page actions. - The <span class="new_code">WebTestCase</span> includes facilities - for navigation, content and cookie checks and form handling. - Usage of these test cases is similar to the - <a href="unit_tester_documentation.html">UnitTestCase</a>... -<pre> -<strong>class TestOfLastcraft extends WebTestCase { -}</strong> -</pre> - Here we are about to test the - <a href="http://www/lastcraft.com/">Last Craft</a> site itself. - If this test case is in a file called <em>lastcraft_test.php</em> - then it can be loaded in a runner script just like unit tests... -<pre> -<?php<strong> - require_once('simpletest/web_tester.php');</strong> - require_once('simpletest/reporter.php'); - - $test = &new GroupTest('Web site tests');<strong> - $test->addTestFile('lastcraft_test.php');</strong> - exit ($test->run(new TextReporter()) ? 0 : 1); -?> -</pre> - I am using the text reporter here to more clearly - distinguish the web content from the test output. - </p> - <p> - Nothing is being tested yet. - We can fetch the home page by using the - <span class="new_code">get()</span> method... -<pre> -class TestOfLastcraft extends WebTestCase { - <strong> - function testHomepage() { - $this->assertTrue($this->get('http://www.lastcraft.com/')); - }</strong> -} -</pre> - The <span class="new_code">get()</span> method will - return true only if page content was successfully - loaded. - It is a simple, but crude way to check that a web page - was actually delivered by the web server. - However that content may be a 404 response and yet - our <span class="new_code">get()</span> method will still return true. - </p> - <p> - Assuming that the web server for the Last Craft site is up - (sadly not always the case), we should see... -<pre class="shell"> -Web site tests -OK -Test cases run: 1/1, Failures: 0, Exceptions: 0 -</pre> - All we have really checked is that any kind of page was - returned. - We don't yet know if it was the right one. - </p> - - <p> -<a class="target" name="content"> -<h2>Testing page content</h2> -</a> -</p> - <p> - To confirm that the page we think we are on is actually the - page we are on, we need to verify the page content. -<pre> -class TestOfLastcraft extends WebTestCase { - - function testHomepage() {<strong> - $this->get('http://www.lastcraft.com/'); - $this->assertWantedPattern('/why the last craft/i');</strong> - } -} -</pre> - The page from the last fetch is held in a buffer in - the test case, so there is no need to refer to it directly. - The pattern match is always made against the buffer. - </p> - <p> - Here is the list of possible content assertions... - <table> -<tbody> - <tr> -<td><span class="new_code">assertTitle($title)</span></td><td>Pass if title is an exact match</td> -</tr> - <tr> -<td><span class="new_code">assertWantedPattern($pattern)</span></td><td>A Perl pattern match against the page content</td> -</tr> - <tr> -<td><span class="new_code">assertNoUnwantedPattern($pattern)</span></td><td>A Perl pattern match to not find content</td> -</tr> - <tr> -<td><span class="new_code">assertWantedText($text)</span></td><td>Pass if matches visible and "alt" text</td> -</tr> - <tr> -<td><span class="new_code">assertNoUnwantedText($text)</span></td><td>Pass if doesn't match visible and "alt" text</td> -</tr> - <tr> -<td><span class="new_code">assertLink($label)</span></td><td>Pass if a link with this text is present</td> -</tr> - <tr> -<td><span class="new_code">assertNoLink($label)</span></td><td>Pass if no link with this text is present</td> -</tr> - <tr> -<td><span class="new_code">assertLinkById($id)</span></td><td>Pass if a link with this id attribute is present</td> -</tr> - <tr> -<td><span class="new_code">assertNoLinkById($id)</span></td><td>Pass if no link with this id attribute is present</td> -</tr> - <tr> -<td><span class="new_code">assertField($name, $value)</span></td><td>Pass if an input tag with this name has this value</td> -</tr> - <tr> -<td><span class="new_code">assertFieldById($id, $value)</span></td><td>Pass if an input tag with this id has this value</td> -</tr> - <tr> -<td><span class="new_code">assertResponse($codes)</span></td><td>Pass if HTTP response matches this list</td> -</tr> - <tr> -<td><span class="new_code">assertMime($types)</span></td><td>Pass if MIME type is in this list</td> -</tr> - <tr> -<td><span class="new_code">assertAuthentication($protocol)</span></td><td>Pass if the current challenge is this protocol</td> -</tr> - <tr> -<td><span class="new_code">assertNoAuthentication()</span></td><td>Pass if there is no current challenge</td> -</tr> - <tr> -<td><span class="new_code">assertRealm($name)</span></td><td>Pass if the current challenge realm matches</td> -</tr> - <tr> -<td><span class="new_code">assertHeader($header, $content)</span></td><td>Pass if a header was fetched matching this value</td> -</tr> - <tr> -<td><span class="new_code">assertNoUnwantedHeader($header)</span></td><td>Pass if a header was not fetched</td> -</tr> - <tr> -<td><span class="new_code">assertHeaderPattern($header, $pattern)</span></td><td>Pass if a header was fetched matching this Perl regex</td> -</tr> - <tr> -<td><span class="new_code">assertCookie($name, $value)</span></td><td>Pass if there is currently a matching cookie</td> -</tr> - <tr> -<td><span class="new_code">assertNoCookie($name)</span></td><td>Pass if there is currently no cookie of this name</td> -</tr> - </tbody> -</table> - As usual with the SimpleTest assertions, they all return - false on failure and true on pass. - They also allow an optional test message and you can embed - the original test message inside using "%s" inside - your custom message. - </p> - <p> - So now we could instead test against the title tag with... -<pre> -<strong>$this->assertTitle('The Last Craft? Web developer tutorials on PHP, Extreme programming and Object Oriented development');</strong> -</pre> - As well as the simple HTML content checks we can check - that the MIME type is in a list of allowed types with... -<pre> -<strong>$this->assertMime(array('text/plain', 'text/html'));</strong> -</pre> - More interesting is checking the HTTP response code. - Like the MIME type, we can assert that the response code - is in a list of allowed values... -<pre> -class TestOfLastcraft extends WebTestCase { - - function testRedirects() { - $this->get('http://www.lastcraft.com/test/redirect.php'); - $this->assertResponse(200);</strong> - } -} -</pre> - Here we are checking that the fetch is successful by - allowing only a 200 HTTP response. - This test will pass, but it is not actually correct to do so. - There is no page, instead the server issues a redirect. - The <span class="new_code">WebTestCase</span> will - automatically follow up to three such redirects. - The tests are more robust this way and we are usually - interested in the interaction with the pages rather - than their delivery. - If the redirects are of interest then this ability must - be disabled... -<pre> -class TestOfLastcraft extends WebTestCase { - - function testHomepage() {<strong> - $this->setMaximumRedirects(0);</strong> - $this->get('http://www.lastcraft.com/test/redirect.php'); - $this->assertResponse(200); - } -} -</pre> - The assertion now fails as expected... -<pre class="shell"> -Web site tests -1) Expecting response in [200] got [302] - in testhomepage - in testoflastcraft - in lastcraft_test.php -FAILURES!!! -Test cases run: 1/1, Failures: 1, Exceptions: 0 -</pre> - We can modify the test to correctly assert redirects with... -<pre> -class TestOfLastcraft extends WebTestCase { - - function testHomepage() { - $this->setMaximumRedirects(0); - $this->get('http://www.lastcraft.com/test/redirect.php'); - $this->assertResponse(<strong>array(301, 302, 303, 307)</strong>); - } -} -</pre> - This now passes. - </p> - - <p> -<a class="target" name="navigation"> -<h2>Navigating a web site</h2> -</a> -</p> - <p> - Users don't often navigate sites by typing in URLs, but by - clicking links and buttons. - Here we confirm that the contact details can be reached - from the home page... -<pre> -class TestOfLastcraft extends WebTestCase { - ... - function testContact() { - $this->get('http://www.lastcraft.com/');<strong> - $this->clickLink('About'); - $this->assertTitle('About Last Craft');</strong> - } -} -</pre> - The parameter is the text of the link. - </p> - <p> - If the target is a button rather than an anchor tag, then - <span class="new_code">clickSubmit()</span> should be used - with the button title... -<pre> -<strong>$this->clickSubmit('Go!');</strong> -</pre> - </p> - <p> - The list of navigation methods is... - <table> -<tbody> - <tr> -<td><span class="new_code">getUrl()</span></td><td>The current location</td> -</tr> - <tr> -<td><span class="new_code">get($url, $parameters)</span></td><td>Send a GET request with these parameters</td> -</tr> - <tr> -<td><span class="new_code">post($url, $parameters)</span></td><td>Send a POST request with these parameters</td> -</tr> - <tr> -<td><span class="new_code">head($url, $parameters)</span></td><td>Send a HEAD request without replacing the page content</td> -</tr> - <tr> -<td><span class="new_code">retry()</span></td><td>Reload the last request</td> -</tr> - <tr> -<td><span class="new_code">back()</span></td><td>Like the browser back button</td> -</tr> - <tr> -<td><span class="new_code">forward()</span></td><td>Like the browser forward button</td> -</tr> - <tr> -<td><span class="new_code">authenticate($name, $password)</span></td><td>Retry after a challenge</td> -</tr> - <tr> -<td><span class="new_code">restart()</span></td><td>Restarts the browser as if a new session</td> -</tr> - <tr> -<td><span class="new_code">getCookie($name)</span></td><td>Gets the cookie value for the current context</td> -</tr> - <tr> -<td><span class="new_code">ageCookies($interval)</span></td><td>Ages current cookies prior to a restart</td> -</tr> - <tr> -<td><span class="new_code">clearFrameFocus()</span></td><td>Go back to treating all frames as one page</td> -</tr> - <tr> -<td><span class="new_code">clickSubmit($label)</span></td><td>Click the first button with this label</td> -</tr> - <tr> -<td><span class="new_code">clickSubmitByName($name)</span></td><td>Click the button with this name attribute</td> -</tr> - <tr> -<td><span class="new_code">clickSubmitById($id)</span></td><td>Click the button with this ID attribute</td> -</tr> - <tr> -<td><span class="new_code">clickImage($label, $x, $y)</span></td><td>Click an input tag of type image by title or alt text</td> -</tr> - <tr> -<td><span class="new_code">clickImageByName($name, $x, $y)</span></td><td>Click an input tag of type image by name</td> -</tr> - <tr> -<td><span class="new_code">clickImageById($id, $x, $y)</span></td><td>Click an input tag of type image by ID attribute</td> -</tr> - <tr> -<td><span class="new_code">submitFormById($id)</span></td><td>Submit a form without the submit value</td> -</tr> - <tr> -<td><span class="new_code">clickLink($label, $index)</span></td><td>Click an anchor by the visible label text</td> -</tr> - <tr> -<td><span class="new_code">clickLinkById($id)</span></td><td>Click an anchor by the ID attribute</td> -</tr> - <tr> -<td><span class="new_code">getFrameFocus()</span></td><td>The name of the currently selected frame</td> -</tr> - <tr> -<td><span class="new_code">setFrameFocusByIndex($choice)</span></td><td>Focus on a frame counting from 1</td> -</tr> - <tr> -<td><span class="new_code">setFrameFocus($name)</span></td><td>Focus on a frame by name</td> -</tr> - </tbody> -</table> - </p> - <p> - The parameters in the <span class="new_code">get()</span>, <span class="new_code">post()</span> or - <span class="new_code">head()</span> methods are optional. - The HTTP HEAD fetch does not change the browser context, only loads - cookies. - This can be useful for when an image or stylesheet sets a cookie - for crafty robot blocking. - </p> - <p> - The <span class="new_code">retry()</span>, <span class="new_code">back()</span> and - <span class="new_code">forward()</span> commands work as they would on - your web browser. - They use the history to retry pages. - This can be handy for checking the effect of hitting the - back button on your forms. - </p> - <p> - The frame methods need a little explanation. - By default a framed page is treated just like any other. - Content will be searced for throughout the entire frameset, - so clicking a link will work no matter which frame - the anchor tag is in. - You can override this behaviour by focusing on a single - frame. - If you do that, all searches and actions will apply to that - frame alone, such as authentication and retries. - If a link or button is not in a focused frame then it cannot - be clicked. - </p> - <p> - Testing navigation on fixed pages only tells you when you - have broken an entire script. - For highly dynamic pages, such as for bulletin boards, this can - be crucial for verifying the correctness of the application. - For most applications though, the really tricky logic is usually in - the handling of forms and sessions. - Fortunately SimpleTest includes - <a href="form_testing_documentation.html">tools for testing web forms</a> - as well. - </p> - - <p> -<a class="target" name="request"> -<h2>Modifying the request</h2> -</a> -</p> - <p> - Although SimpleTest does not have the goal of testing networking - problems, it does include some methods to modify and debug - the requests it makes. - Here is another method list... - <table> -<tbody> - <tr> -<td><span class="new_code">getTransportError()</span></td><td>The last socket error</td> -</tr> - <tr> -<td><span class="new_code">showRequest()</span></td><td>Dump the outgoing request</td> -</tr> - <tr> -<td><span class="new_code">showHeaders()</span></td><td>Dump the incoming headers</td> -</tr> - <tr> -<td><span class="new_code">showSource()</span></td><td>Dump the raw HTML page content</td> -</tr> - <tr> -<td><span class="new_code">ignoreFrames()</span></td><td>Do not load framesets</td> -</tr> - <tr> -<td><span class="new_code">setCookie($name, $value)</span></td><td>Set a cookie from now on</td> -</tr> - <tr> -<td><span class="new_code">addHeader($header)</span></td><td>Always add this header to the request</td> -</tr> - <tr> -<td><span class="new_code">setMaximumRedirects($max)</span></td><td>Stop after this many redirects</td> -</tr> - <tr> -<td><span class="new_code">setConnectionTimeout($timeout)</span></td><td>Kill the connection after this time between bytes</td> -</tr> - <tr> -<td><span class="new_code">useProxy($proxy, $name, $password)</span></td><td>Make requests via this proxy URL</td> -</tr> - </tbody> -</table> - These methods are principally for debugging. - </p> - - </div> -<div class="copyright"> - Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004 - </div> -</body> -</html> |