diff options
Diffstat (limited to 'vendors/simpletest/docs/en')
-rw-r--r-- | vendors/simpletest/docs/en/authentication_documentation.html | 355 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/browser_documentation.html | 447 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/docs.css | 121 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/expectation_documentation.html | 422 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/form_testing_documentation.html | 342 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/group_test_documentation.html | 386 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/index.html | 538 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/mock_objects_documentation.html | 757 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/overview.html | 486 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/partial_mocks_documentation.html | 445 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/reporter_documentation.html | 519 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/unit_test_documentation.html | 431 | ||||
-rw-r--r-- | vendors/simpletest/docs/en/web_tester_documentation.html | 584 |
13 files changed, 5833 insertions, 0 deletions
diff --git a/vendors/simpletest/docs/en/authentication_documentation.html b/vendors/simpletest/docs/en/authentication_documentation.html new file mode 100644 index 000000000..8da2a7f9e --- /dev/null +++ b/vendors/simpletest/docs/en/authentication_documentation.html @@ -0,0 +1,355 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <span class="chosen">Authentication</span> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Authentication documentation</h1> + This page... + <ul> +<li> + Getting through <a href="#basic">Basic HTTP authentication</a> + </li> +<li> + Testing <a href="#cookies">cookie based authentication</a> + </li> +<li> + Managing <a href="#session">browser sessions</a> and timeouts + </li> +</ul> +<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> + One theme that runs through SimpleTest is the ability to use + <span class="new_code">SimpleExpectation</span> objects wherever a simple + match is not enough. + If you want only an approximate match to the realm for + example, you can do this... +<pre> +class AuthenticationTest extends WebTestCase { + function test401Header() { + $this->get('http://www.lastcraft.com/protected/'); + $this->assertRealm(<strong>new PatternExpectation('/simpletest/i')</strong>); + } +} +</pre> + 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 testCanAuthenticate() { + $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 with... +<pre> +class LogInTest extends WebTestCase { + function testSessionCookieIsCorrectPattern() { + $this->get('http://www.my-site.com/login.php'); + $this->assertCookie('SID', <strong>new PatternExpectation('/[a-f0-9]{32}/i')</strong>); + } +} +</pre> + 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->click('Log in'); + $this->assertText('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->assertText('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->click('Log in'); + $this->assertText('Welcome Me');<strong> + + $this->restart(); + $this->get('http://www.my-site.com/restricted.php'); + $this->assertText('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->click('Log in'); + $this->assertText('Welcome Me'); + <strong> + $this->ageCookies(3600);</strong> + $this->restart(); + $this->get('http://www.my-site.com/restricted.php'); + $this->assertText('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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <span class="chosen">Authentication</span> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/browser_documentation.html b/vendors/simpletest/docs/en/browser_documentation.html new file mode 100644 index 000000000..522f3a598 --- /dev/null +++ b/vendors/simpletest/docs/en/browser_documentation.html @@ -0,0 +1,447 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <span class="chosen">Scriptable browser</span> +</div></div> +<h1>PHP Scriptable Web Browser</h1> + This page... + <ul> +<li> + Using the bundled <a href="#scripting">web browser in scripts</a> + </li> +<li> + <a href="#debug">Debugging</a> failed pages + </li> +<li> + Complex <a href="#unit">tests with multiple web browsers</a> + </li> +</ul> +<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->click('reporting bugs'); +$browser->click('statistics'); +$page = $browser->click('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">clickLinkById($id)</span></td> +<td>Follows a link by attribute</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 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>Clicks an input tag of type image by name</td> +</tr> + <tr> +<td><span class="new_code">clickImageById($id, $x, $y)</span></td> +<td>Clicks an input tag of type image by ID 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> + <tr> +<td><span class="new_code">ignoreCookies()</span></td> +<td>Disables sending and receiving of cookies</td> +</tr> + <tr> +<td><span class="new_code">useCookies()</span></td> +<td>Enables cookie 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>, + <span class="new_code">SimpleBrowser::useFrames()</span>, + <span class="new_code">SimpleBrowser::ignoreCookies()</span> and + <span class="new_code">SimpleBrowser::useCokies()</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->click('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->click('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->click('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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <span class="chosen">Scriptable browser</span> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/docs.css b/vendors/simpletest/docs/en/docs.css new file mode 100644 index 000000000..18368a04f --- /dev/null +++ b/vendors/simpletest/docs/en/docs.css @@ -0,0 +1,121 @@ +body { + padding-left: 3%; + padding-right: 3%; +} +h1, h2, h3 { + font-family: sans-serif; +} +h1 { + text-align: center; +} +pre { + font-family: "courier new", courier, typewriter, monospace; + font-size: 90%; + border: 1px solid; + border-color: #999966; + background-color: #ffffcc; + padding: 5px; + margin-left: 20px; + margin-right: 40px; +} +.code, .new_code, pre.new_code { + font-family: "courier new", courier, typewriter, monospace; + font-weight: bold; +} +div.copyright { + font-size: 80%; + color: gray; +} +div.copyright a { + margin-top: 1em; + color: gray; +} +ul.api { + border: 2px outset; + border-color: gray; + background-color: white; + margin: 5px; + margin-left: 5%; + margin-right: 5%; +} +ul.api li { + margin-top: 0.2em; + margin-bottom: 0.2em; + list-style: none; + text-indent: -3em; + padding-left: 1em; +} +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; +} +div.menu { + text-align: center; +} +table { + border: 2px outset; + border-color: gray; + background-color: white; + margin: 5px; + margin-left: 5%; + margin-right: 5%; +} +td { + font-size: 90%; +} +.shell { + color: white; +} +pre.shell { + border: 4px ridge; + border-color: gray; + padding: 10px; + margin: 5px; + margin-left: 20px; + margin-right: 40px; + background-color: #000100; + color: #99ff99; + font-size: 90%; +} +pre.file { + color: black; + border: 1px solid; + border-color: black; + padding: 10px; + margin: 5px; + margin-left: 20px; + margin-right: 40px; + background-color: white; + font-size: 90%; +} +form.demo { + background-color: lightgray; + border: 4px outset; + border-color: lightgray; + padding: 10px; + margin-right: 40%; +} +dl, dd { + margin: 10px; + margin-left: 30px; +} +em { + font-weight: bold; + font-family: "courier new", courier, typewriter, monospace; +} diff --git a/vendors/simpletest/docs/en/expectation_documentation.html b/vendors/simpletest/docs/en/expectation_documentation.html new file mode 100644 index 000000000..c3c959c4c --- /dev/null +++ b/vendors/simpletest/docs/en/expectation_documentation.html @@ -0,0 +1,422 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <span class="chosen">Expectations</span> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Expectation documentation</h1> + This page... + <ul> +<li> + Using expectations for + <a href="#mock">more precise testing with mock objects</a> + </li> +<li> + <a href="#behaviour">Changing mock object behaviour</a> with expectations + </li> +<li> + <a href="#extending">Extending the expectations</a> + </li> +<li> + Underneath SimpleTest <a href="#unit">uses expectation classes</a> + </li> +</ul> +<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(); + $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);</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();<strong> + $writer->expectOnce( + 'write', + array(new PatternExpectation('/cannot connect/i')));</strong> + + $service = &new NewsService('BBC News'); + $service->publish($writer); + } +} +</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">AnythingExpectation</span></td> +<td>Will always match</td> +</tr> + <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">WithinMarginExpectation</span></td> +<td>Compares a value to within a margin</td> +</tr> + <tr> +<td><span class="new_code">OutsideMarginExpectation</span></td> +<td>Checks that a value is out side the margin</td> +</tr> + <tr> +<td><span class="new_code">PatternExpectation</span></td> +<td>Uses a Perl Regex to match a string</td> +</tr> + <tr> +<td><span class="new_code">NoPatternExpectation</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> + Some examples... + </p> + <p> +<pre> +$mock->expectOnce('method', array(new IdenticalExpectation(14))); +</pre> + This is the same as <span class="new_code">$mock->expectOnce('method', array(14))</span>. +<pre> +$mock->expectOnce('method', array(new EqualExpectation(14))); +</pre> + This is different from the previous version in that the string + <span class="new_code">"14"</span> as a parameter will also pass. + Sometimes the additional type checks of SimpleTest are too restrictive. +<pre> +$mock->expectOnce('method', array(new AnythingExpectation(14))); +</pre> + This is the same as <span class="new_code">$mock->expectOnce('method', array('*'))</span>. +<pre> +$mock->expectOnce('method', array(new IdenticalExpectation('*'))); +</pre> + This is handy if you want to assert a literal <span class="new_code">"*"</span>. +<pre> +new NotIdenticalExpectation(14) +</pre> + This matches on anything other than integer 14. + Even the string <span class="new_code">"14"</span> would pass. +<pre> +new WithinMarginExpectation(14.0, 0.001) +</pre> + This will accept any value from 13.999 to 14.001 inclusive. + </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 the + <a href="mock_objects_documentation.html">mock objects</a>. + Anywhere a list of arguments is given, a list of expectation objects + can be inserted instead. + </p> + <p> + Suppose we want a mock authorisation server to simulate a successful login, + but only if it receives a valid session object. + We can do this as follows... +<pre> +Mock::generate('Authorisation'); +<strong> +$authorisation = new MockAuthorisation(); +$authorisation->setReturnValue( + 'isAllowed', + true, + array(new IsAExpectation('Session', 'Must be a session'))); +$authorisation->setReturnValue('isAllowed', false);</strong> +</pre> + We have set the default mock 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::assert()</span> method to + test against it directly... +<pre> +<strong>class TestOfNetworking extends UnitTestCase { + ... + function testGetValidIp() { + $server = &new Server(); + $this->assert( + 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->assert(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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The expectations mimic the constraints in <a href="http://www.jmock.org/">JMock</a>. + </li> +<li> + <a href="http://simpletest.org/api/">Full API for SimpleTest</a> + from the PHPDoc. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <span class="chosen">Expectations</span> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/form_testing_documentation.html b/vendors/simpletest/docs/en/form_testing_documentation.html new file mode 100644 index 000000000..fe0fcccf0 --- /dev/null +++ b/vendors/simpletest/docs/en/form_testing_documentation.html @@ -0,0 +1,342 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <span class="chosen">Testing forms</span> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Form testing documentation</h1> + This page... + <ul> +<li> + Changing form values and successfully + <a href="#submit">Submitting a simple form</a> + </li> +<li> + Handling <a href="#multiple">widgets with multiple values</a> + by setting lists. + </li> +<li> + Bypassing javascript to <a href="#hidden-field">set a hidden field</a>. + </li> +<li> + <a href="#raw">Raw posting</a> when you don't have a button + to click. + </li> +</ul> +<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". + As usual, we could use a pattern expectation instead if a fixed + string. + </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->click('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> + The browser emulation offered by SimpleTest mimics + the actions which can be perform by a user on a + standard HTML page. Javascript is not supported, and + it's unlikely that support will be added any time + soon. + </p> + <p> + Of particular note is that the Javascript idiom of + passing form results by setting a hidden field cannot + be performed using the normal SimpleTest + commands. See below for a way to test such forms. + </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->click('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="hidden-field"><h2>Forms which use javascript to set a hidden field</h2></a></p> + <p> + If you want to test a form which relies on javascript to set a hidden + field, you can't just call setField(). + The following code will <em>not</em> work: +<pre> +class SimpleFormTests extends WebTestCase { + function testMyJavascriptForm() { + <strong>// This does *not* work</strong> + $this->setField('a_hidden_field', '123'); + $this->clickSubmit('OK'); + } +} +</pre> + Instead, you need to pass the additional form parameters to the + clickSubmit() method: +<pre> +class SimpleFormTests extends WebTestCase { + function testMyJavascriptForm() { + // Pass the hidden field value as an additional POST variable + <strong>$this->clickSubmit('OK', array('a_hidden_field'=>'123'));</strong> + } + +} +</pre> + </p> + <p> + Bear in mind that in doing this you're effectively stubbing out a + part of your software (the javascript code in the form), and + perhaps you might be better off using something like + <a href="http://selenium.openqa.org/">Selenium</a> to ensure a complete + acceptance test. + </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->assertNoText('user created'); + }</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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <span class="chosen">Testing forms</span> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/group_test_documentation.html b/vendors/simpletest/docs/en/group_test_documentation.html new file mode 100644 index 000000000..a0c78843c --- /dev/null +++ b/vendors/simpletest/docs/en/group_test_documentation.html @@ -0,0 +1,386 @@ +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>SimpleTest for PHP test suites</title> +<link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> +</head> +<body> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <span class="chosen">Group tests</span> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Test suite documentation</h1> + This page... + <ul> +<li> + Different ways to <a href="#group">group tests</a> together. + </li> +<li> + Combining group tests into <a href="#higher">larger groups</a>. + </li> +<li> + Integrating <a href="#legacy">legacy test cases</a> from other + types of PHPUnit. + </li> +</ul> +<div class="content"> + <p><a class="target" name="group"><h2>Grouping tests into suites</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. In PHP 4... +<pre> +<?php + require_once('../classes/io.php'); +<strong> + class MyFileTestCase extends UnitTestCase { + ... + } + SimpleTest::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). + </p> + <p> + If you are using PHP 5, you do not need this special directive at all. + Simply mark any test cases that should not be run as abstract... +<pre> +<strong>abstract</strong> class MyFileTestCase extends UnitTestCase { + ... +} + +class FileTester extends MyFileTestCase { ... } + +class SocketTester extends UnitTestCase { ... } +</pre> + </p> + <p> + We will call this sample <em>file_test.php</em>. + Next we create a group test file, called say <em>my_group_test.php</em>. + You will think of a better name I am sure. + </p> + <p> + 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 TestSuite('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, and can be surprising behaviour. + </p> + <p> + The main problem 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. + </p> + <p> + We can save a lot of typing with... +<pre> +<?php + require_once('simpletest/unit_tester.php'); + require_once('simpletest/reporter.php'); + + $test = &new TestSuite('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">TestSuite</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> + No only that, but you can guarantee that the constructor is run + just before the first test method and, in PHP 5, the destructor + is run just after the last test method. + </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 (Note : with the new <cite>autorun</cite> + functionnality, this problem has now been solved). + </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">TestSuite::addTestFile()</span> + line, or make sure that they are abstract classes. + </li> + </ol> + </p> + + <p><a class="target" name="higher"><h2>Composite suites</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">TestSuite</span> and then instantiate it as needed... +<pre> +<?php + require_once('simpletest/unit_tester.php'); + require_once('simpletest/reporter.php'); + <strong> + class FileTestSuite extends TestSuite { + function FileTestSuite() { + $this->TestSuite('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_test_suite.php'); + <strong> + $test = &new FileTestSuite(); + $test->run(new HtmlReporter());</strong> +?> +</pre> + ...or we can group them into even larger group tests. + We can even mix groups and test cases freely as long as + we are careful about double includes... +<pre> +<?php + <strong> + $test = &new BigTestSuite('Big group'); + $test->addTestFile('file_test_suite.php'); + $test->addTestFile('some_test_case.php');</strong> + $test->run(new HtmlReporter()); +?> +</pre> + In the event of a double include, ony the first instance + of the test case will be run. + </p> + <p> + 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 FileTestSuite extends TestSuite { + function FileTestSuite() { + $this->TestSuite('All file tests'); + $test->addTestFile('file_test.php'); + } + } + <strong> + if (! defined('RUNNER')) { + define('RUNNER', true);</strong> + $test = &new FileTestSuite(); + $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_test_suite.php'); + + $test = &new BigTestSuite('Big group'); + $test->addTestCase(new FileTestSuite()); + $test->addTestCase(...); + $test->run(new HtmlReporter()); +?> +</pre> + As with the normal test cases, a <span class="new_code">TestSuite</span> can + be loaded with the <span class="new_code">TestSuite::addTestFile()</span> method. +<pre> +<?php + define('RUNNER', true); + + $test = &new BigTestSuite('Big group');<strong> + $test->addTestFile('file_test_suite.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 TestSuite('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 TestSuite('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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <span class="chosen">Group tests</span> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/index.html b/vendors/simpletest/docs/en/index.html new file mode 100644 index 000000000..03b6c5cef --- /dev/null +++ b/vendors/simpletest/docs/en/index.html @@ -0,0 +1,538 @@ +<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"> +<span class="chosen">SimpleTest</span> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Simple Test for PHP</h1> + This page... + <ul> +<li> + <a href="#unit">Using unit tester</a> + with an example. + </li> +<li> + <a href="#group">Grouping tests</a> + for testing with one click. + </li> +<li> + <a href="#mock">Using mock objects</a> + to ease testing and gain tighter control. + </li> +<li> + <a href="#web">Testing web pages</a> + at the browser level. + </li> +</ul> +<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, and includes + <a href="http://www.mockobjects.com">mock objects</a>. + </p> + <p> + What makes this tool immediately useful to the PHP developer is the internal + web browser. + This allows tests that navigate web sites, fill in forms and test pages. + Being able to write these test in PHP means that it is easy to write + integrated tests. + An example might be confirming that a user was written to a database + after a signing up through the web site. + </p> + <p> + The quickest way to demonstrate SimpleTest 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> +<?php +<strong>require_once('simpletest/autorun.php');</strong> +require_once('../classes/log.php'); + +class TestOfLogging extends <strong>UnitTestCase</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 + unpacked the toolset. + The "autorun.php" file does more than just include the + SimpleTest files, it also runs our test for us. + </p> + <p> + The <span class="new_code">TestOfLogging</span> is our first test case and it's + currently empty. + Each test case is a class that extends one of the SimpleTet base classes + and we can have as many of these in the file as we want. + </p> + <p> + With three lines of scaffolding, and our <span class="new_code">Log</span> class + include, we have a test suite. + No tests though. + </p> + <p> + For our first test, 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/autorun.php'); +require_once('../classes/log.php'); + +class TestOfLogging extends UnitTestCase { + function <strong>testLogCreatesNewFileOnFirstMessage()</strong> { + @unlink('/temp/test.log'); + $log = new Log('/temp/test.log'); + <strong>$this->assertFalse(file_exists('/temp/test.log'));</strong> + $log->message('Should write this to a file'); + <strong>$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 "test" + and execute that method. + If the method starts "test", it's a test. + Note the very long name <span class="new_code">testLogCreatesNewFileOnFirstMessage()</span>. + This is considered good style and makes the test output more readable. + </p> + <p> + We would normally have more than one test method in a test case, + but that's for later. + </p> + <p> + 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 debugging content right next to the assertion concerned. + </p> + <p> + To see these results we have to actually run the tests. + No other code is necessary - we can just open the page + with our browser. + </p> + <p> + On failure the display looks like this... + <div class="demo"> + <h1>TestOfLogging</h1> + <span class="fail">Fail</span>: testLogCreatesNewFileOnFirstMessage->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<strong> +class Log { + function Log($file_path) { + } + + function message() { + } +}</strong> +?> +</pre> + It's fun to write the code after the test. + More than fun even - + this system is called "Test Driven Development". + </p> + <p> + For more information about <span class="new_code">UnitTestCase</span>, see + the <a href="unit_test_documentation.html">unit test documentation</a>. + </p> + + <p><a class="target" name="group"><h2>Building test suites</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 for the application. + </p> + <p> + Our first step is to create a new file called <em>tests/all_tests.php</em> + and insert the following code... +<pre> +<?php +<strong>require_once('simpletest/autorun.php');</strong> + +class AllTests extends <strong>TestSuite</strong> { + function AllTests() { + $this->TestSuite(<strong>'All tests'</strong>); + <strong>$this->addFile('log_test.php');</strong> + } +} +?> +</pre> + The "autorun" include allows our upcoming test suite + to be run just by invoking this script. + </p> + <p> + The <span class="new_code">TestSuite</span> subclass must chain it's constructor. + This limitation will be removed in future versions. + </p> + <p> + The method <span class="new_code">TestSuite::addFile()</span> + will include the test case file and read any new classes + that are descended from <span class="new_code">SimpleTestCase</span>. + <span class="new_code">UnitTestCase</span> is just one example of a class derived from + <span class="new_code">SimpleTestCase</span>, and you can create your own. + <span class="new_code">TestSuite::addFile()</span> can include other test suites. + </p> + <p> + The class will not be instantiated yet. + When the test suite runs it will construct each instance once + it reaches that test, then destroy it straight after. + This means that the constructor is run just before each run + of that test case, and the destructor is run before the next test case starts. + </p> + <p> + It is common to group test case code into superclasses which are not + supposed to run, but become the base classes of other tests. + For "autorun" to work properly the test case file should not blindly run + 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 mark your + base class as <span class="new_code">abstract</span>. + SimpleTest won't run abstract classes. + If you are still using PHP4, then + a <span class="new_code">SimpleTestOptions::ignore()</span> directive + somewhere in the test case file will have the same effect. + </p> + <p> + 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">TestSuite::addFile()</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 or the command line. + </p> + <p> + For more information about building test suites, + see the <a href="group_test_documentation.html">test suite documentation</a>. + </p> + + <p><a class="target" name="mock"><h2>Using mock objects</h2></a></p> + <p> + Let's move further into the future and do something really complicated. + </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> +<?php +require_once('simpletest/autorun.php'); +require_once('../classes/log.php'); +<strong>require_once('../classes/session_pool.php');</strong> + +class <strong>TestOfSessionLogging</strong> extends UnitTestCase { + + function setUp() { + <strong>@unlink('/temp/test.log');</strong> + } + + function tearDown() { + <strong>@unlink('/temp/test.log');</strong> + } + + function testLoggingInIsLogged() { + <strong>$log = new Log('/temp/test.log'); + $session_pool = &new SessionPool($log); + $session_pool->logIn('fred');</strong> + $messages = file('/temp/test.log'); + $this->assertEqual($messages[0], "User fred logged in.<strong>\n</strong>"); + } +} +?> +</pre> + We'll explain the <span class="new_code">setUp()</span> and <span class="new_code">tearDown()</span> + methods later. + </p> + <p> + 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. + 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? + It means that our <span class="new_code">TestOfSessionLogging</span> test will + fail, even thouh it's not testing Logging. + </p> + <p> + It's fragile in smaller ways too. + 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 can reduce coupling if we 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? + We can create such an object easily... +<pre> +<?php +require_once('simpletest/autorun.php'); +require_once('../classes/log.php'); +require_once('../classes/session_pool.php'); + +<strong>Mock::generate('Log');</strong> + +class TestOfSessionLogging extends UnitTestCase { + + function testLoggingInIsLogged() {<strong> + $log = &new MockLog(); + $log->expectOnce('message', array('User fred logged in.'));</strong> + $session_pool = &new SessionPool(<strong>$log</strong>); + $session_pool->logIn('fred'); + } +} +?> +</pre> + The <span class="new_code">Mock::generate()</span> call code generated a new class + called <span class="new_code">MockLog</span>. + This looks like an identical clone, except that we can wire test code + to it. + That's what <span class="new_code">expectOnce()</span> does. + It says that if <span class="new_code">message()</span> is ever called on me, it had + better be with the parameter "User fred logged in.". + </p> + <p> + The 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 by <span class="new_code">SessionPool::logIn()</span> code. + 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 you don't have to test every parameter of + a call when you only want to test one. + </p> + <p> + If the mock reaches the end of the test case without the + method being called, the <span class="new_code">expectOnce()</span> + expectation will trigger a test failure. + In other words the mocks can detect the absence of + behaviour as well as the presence. + </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 more information about mocking and stubbing, see the + <a href="mock_objects_documentation.html">mock objects documentation</a>. + </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, as there is + no JavaScript. + Most other browser operations are simulated. + </p> + <p> + 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 +require_once('simpletest/autorun.php'); +<strong>require_once('simpletest/web_tester.php');</strong> + +class TestOfAbout extends <strong>WebTestCase</strong> { + function testOurAboutPageGivesFreeReignToOurEgo() { + <strong>$this->get('http://test-server/index.php'); + $this->click('About'); + $this->assertTitle('About why we are so great'); + $this->assertText('We are really great');</strong> + } +} +?> +</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> + You can navigate forms too... +<pre> +<?php +require_once('simpletest/autorun.php'); +require_once('simpletest/web_tester.php'); + +class TestOfRankings extends WebTestCase { + function testWeAreTopOfGoogle() { + $this->get('http://google.com/'); + $this->setField('q', 'simpletest'); + $this->click("I'm Feeling Lucky"); + $this->assertTitle('SimpleTest - Unit Testing for PHP'); + } +} +?> +</pre> + ...although this could violate Google's(tm) terms and conditions. + </p> + <p> + For more information about web testing, see the + <a href="browser_documentation.html">scriptable + browser documentation</a> and the + <a href="web_tester_documentation.html">WebTestCase</a>. + </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> + References and related information... + <ul> +<li> + <a href="https://sourceforge.net/project/showfiles.php?group_id=76550&release_id=153280">Download PHP Simple Test</a> + from <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<span class="chosen">SimpleTest</span> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/mock_objects_documentation.html b/vendors/simpletest/docs/en/mock_objects_documentation.html new file mode 100644 index 000000000..c3d002277 --- /dev/null +++ b/vendors/simpletest/docs/en/mock_objects_documentation.html @@ -0,0 +1,757 @@ +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>SimpleTest for PHP mock objects documentation</title> +<link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> +</head> +<body> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <span class="chosen">Mock objects</span> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Mock objects documentation</h1> + This page... + <ul> +<li> + <a href="#what">What are mock objects?</a> + </li> +<li> + <a href="#creation">Creating mock objects</a>. + </li> +<li> + <a href="#stub">Mocks as actors</a> or stubs. + </li> +<li> + <a href="#expectations">Mocks as critics</a> with expectations. + </li> +<li> + <a href="#approaches">Other approaches</a> including mock libraries. + </li> +</ul> +<div class="content"> + <p><a class="target" name="what"><h2>What are mock objects?</h2></a></p> + <p> + Mock objects have two roles during a test case: actor and critic. + </p> + <p> + The actor behaviour is to simulate objects that are difficult to + set up or time consuming to set up for a test. + The classic example is a database connection. + Setting up a test database at the start of each test would slow + testing to a crawl and would require the installation of the + database engine and test data on the test machine. + If we can simulate the connection and return data of our + choosing we not only win on the pragmatics of testing, but can + also feed our code spurious data to see how it responds. + We can simulate databases being down or other extremes + without having to create a broken database for real. + In other words, we get greater control of the test environment. + </p> + <p> + If mock objects only behaved as actors they would simply be + known as server stubs. + This was originally a pattern named by Robert Binder (Testing + object-oriented systems: models, patterns, and tools, + Addison-Wesley) in 1999. + </p> + <p> + A server stub is a simulation of an object or component. + It should exactly replace a component in a system for test + or prototyping purposes, but remain lightweight. + This allows tests to run more quickly, or if the simulated + class has not been written, to run at all. + </p> + <p> + However, the mock objects not only play a part (by supplying chosen + return values on demand) they are also sensitive to the + messages sent to them (via expectations). + By setting expected parameters for a method call they act + as a guard that the calls upon them are made correctly. + If expectations are not met they save us the effort of + writing a failed test assertion by performing that duty on our + behalf. + </p> + <p> + In the case of an imaginary database connection they can + test that the query, say SQL, was correctly formed by + the object that is using the connection. + Set them up with fairly tight expectations and you will + hardly need manual assertions at all. + </p> + + <p><a class="target" name="creation"><h2>Creating mock objects</h2></a></p> + <p> + In the same way that we create server stubs, all we need is an + existing class, say a database connection that looks like this... +<pre> +<strong>class DatabaseConnection { + function DatabaseConnection() { + } + + function query() { + } + + function selectQuery() { + } +}</strong> +</pre> + The class does not need to have been implemented yet. + To create a mock version of the class we need to include the + mock object library and run the generator... +<pre> +<strong>require_once('simpletest/unit_tester.php'); +require_once('simpletest/mock_objects.php'); +require_once('database_connection.php'); + +Mock::generate('DatabaseConnection');</strong> +</pre> + This generates a clone class called + <span class="new_code">MockDatabaseConnection</span>. + We can now create instances of the new class within + our test case... +<pre> +require_once('simpletest/unit_tester.php'); +require_once('simpletest/mock_objects.php'); +require_once('database_connection.php'); + +Mock::generate('DatabaseConnection'); +<strong> +class MyTestCase extends UnitTestCase { + + function testSomething() { + $connection = &new MockDatabaseConnection(); + } +}</strong> +</pre> + Unlike the generated stubs the mock constructor needs a reference + to the test case so that it can dispatch passes and failures while + checking its expectations. + This means that mock objects can only be used within test cases. + Despite this their extra power means that stubs are hardly ever used + if mocks are available. + </p> + <p> + <a class="target" name="stub"><h2>Mocks as actors</h2></a> + </p> + <p> + The mock version of a class has all the methods of the original, + so that operations like + <span class="new_code">$connection->query()</span> are still + legal. + The return value will be <span class="new_code">null</span>, + but we can change that with... +<pre> +<strong>$connection->setReturnValue('query', 37)</strong> +</pre> + Now every time we call + <span class="new_code">$connection->query()</span> we get + the result of 37. + We can set the return value to anything, say a hash of + imaginary database results or a list of persistent objects. + Parameters are irrelevant here, we always get the same + values back each time once they have been set up this way. + That may not sound like a convincing replica of a + database connection, but for the half a dozen lines of + a test method it is usually all you need. + </p> + <p> + We can also add extra methods to the mock when generating it + and choose our own class name... +<pre> +<strong>Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));</strong> +</pre> + Here the mock will behave as if the <span class="new_code">setOptions()</span> + existed in the original class. + This is handy if a class has used the PHP <span class="new_code">overload()</span> + mechanism to add dynamic methods. + You can create a special mock to simulate this situation. + </p> + <p> + Things aren't always that simple though. + One common problem is iterators, where constantly returning + the same value could cause an endless loop in the object + being tested. + For these we need to set up sequences of values. + Let's say we have a simple iterator that looks like this... +<pre> +class Iterator { + function Iterator() { + } + + function next() { + } +} +</pre> + This is about the simplest iterator you could have. + Assuming that this iterator only returns text until it + reaches the end, when it returns false, we can simulate it + with... +<pre> +Mock::generate('Iterator'); + +class IteratorTest extends UnitTestCase() { + + function testASequence() {<strong> + $iterator = &new MockIterator(); + $iterator->setReturnValue('next', false); + $iterator->setReturnValueAt(0, 'next', 'First string'); + $iterator->setReturnValueAt(1, 'next', 'Second string');</strong> + ... + } +} +</pre> + When <span class="new_code">next()</span> is called on the + mock iterator it will first return "First string", + on the second call "Second string" will be returned + and on any other call <span class="new_code">false</span> will + be returned. + The sequenced return values take precedence over the constant + return value. + The constant one is a kind of default if you like. + </p> + <p> + Another tricky situation is an overloaded + <span class="new_code">get()</span> operation. + An example of this is an information holder with name/value pairs. + Say we have a configuration class like... +<pre> +class Configuration { + function Configuration() { + } + + function getValue($key) { + } +} +</pre> + This is a classic situation for using mock objects as + actual configuration will vary from machine to machine, + hardly helping the reliability of our tests if we use it + directly. + The problem though is that all the data comes through the + <span class="new_code">getValue()</span> method and yet + we want different results for different keys. + Luckily the mocks have a filter system... +<pre> +<strong>$config = &new MockConfiguration(); +$config->setReturnValue('getValue', 'primary', array('db_host')); +$config->setReturnValue('getValue', 'admin', array('db_user')); +$config->setReturnValue('getValue', 'secret', array('db_password'));</strong> +</pre> + The extra parameter is a list of arguments to attempt + to match. + In this case we are trying to match only one argument which + is the look up key. + Now when the mock object has the + <span class="new_code">getValue()</span> method invoked + like this... +<pre> +$config->getValue('db_user') +</pre> + ...it will return "admin". + It finds this by attempting to match the calling arguments + to its list of returns one after another until + a complete match is found. + </p> + <p> + You can set a default argument argument like so... +<pre><strong> +$config->setReturnValue('getValue', false, array('*'));</strong> +</pre> + This is not the same as setting the return value without + any argument requirements like this... +<pre><strong> +$config->setReturnValue('getValue', false);</strong> +</pre> + In the first case it will accept any single argument, + but exactly one is required. + In the second case any number of arguments will do and + it acts as a catchall after all other matches. + Note that if we add further single parameter options after + the wildcard in the first case, they will be ignored as the wildcard + will match first. + With complex parameter lists the ordering could be important + or else desired matches could be masked by earlier wildcard + ones. + Declare the most specific matches first if you are not sure. + </p> + <p> + There are times when you want a specific object to be + dished out by the mock rather than a copy. + The PHP4 copy semantics force us to use a different method + for this. + You might be simulating a container for example... +<pre> +class Thing { +} + +class Vector { + function Vector() { + } + + function get($index) { + } +} +</pre> + In this case you can set a reference into the mock's + return list... +<pre> +$thing = &new Thing();<strong> +$vector = &new MockVector(); +$vector->setReturnReference('get', $thing, array(12));</strong> +</pre> + With this arrangement you know that every time + <span class="new_code">$vector->get(12)</span> is + called it will return the same + <span class="new_code">$thing</span> each time. + This is compatible with PHP5 as well. + </p> + <p> + These three factors, timing, parameters and whether to copy, + can be combined orthogonally. + For example... +<pre> +$complex = &new MockComplexThing(); +$stuff = &new Stuff();<strong> +$complex->setReturnReferenceAt(3, 'get', $stuff, array('*', 1));</strong> +</pre> + This will return the <span class="new_code">$stuff</span> only on the third + call and only if two parameters were set the second of + which must be the integer 1. + That should cover most simple prototyping situations. + </p> + <p> + A final tricky case is one object creating another, known + as a factory pattern. + Suppose that on a successful query to our imaginary + database, a result set is returned as an iterator with + each call to <span class="new_code">next()</span> giving + one row until false. + This sounds like a simulation nightmare, but in fact it can all + be mocked using the mechanics above. + </p> + <p> + Here's how... +<pre> +Mock::generate('DatabaseConnection'); +Mock::generate('ResultIterator'); + +class DatabaseTest extends UnitTestCase { + + function testUserFinder() {<strong> + $result = &new MockResultIterator(); + $result->setReturnValue('next', false); + $result->setReturnValueAt(0, 'next', array(1, 'tom')); + $result->setReturnValueAt(1, 'next', array(3, 'dick')); + $result->setReturnValueAt(2, 'next', array(6, 'harry')); + + $connection = &new MockDatabaseConnection(); + $connection->setReturnValue('query', false); + $connection->setReturnReference( + 'query', + $result, + array('select id, name from users'));</strong> + + $finder = &new UserFinder($connection); + $this->assertIdentical( + $finder->findNames(), + array('tom', 'dick', 'harry')); + } +} +</pre> + Now only if our + <span class="new_code">$connection</span> is called with the correct + <span class="new_code">query()</span> will the + <span class="new_code">$result</span> be returned that is + itself exhausted after the third call to <span class="new_code">next()</span>. + This should be enough + information for our <span class="new_code">UserFinder</span> class, + the class actually + being tested here, to come up with goods. + A very precise test and not a real database in sight. + </p> + + <p><a class="target" name="expectations"><h2>Mocks as critics</h2></a></p> + <p> + Although the server stubs approach insulates your tests from + real world disruption, it is only half the benefit. + You can have the class under test receiving the required + messages, but is your new class sending correct ones? + Testing this can get messy without a mock objects library. + </p> + <p> + By way of example, suppose we have a + <span class="new_code">SessionPool</span> class that we + want to add logging to. + Rather than grow the original class into something more + complicated, we want to add this behaviour with a decorator (GOF). + The <span class="new_code">SessionPool</span> code currently looks + like this... +<pre> +<strong>class SessionPool { + function SessionPool() { + ... + } + + function &findSession($cookie) { + ... + } + ... +} + +class Session { + ... +}</strong> +</pre> + While our logging code looks like this... +<pre> +<strong> +class Log { + function Log() { + ... + } + + function message() { + ... + } +} + +class LoggingSessionPool { + function LoggingSessionPool(&$session_pool, &$log) { + ... + } + + function &findSession($cookie) { + ... + } + ... +}</strong> +</pre> + Out of all of this, the only class we want to test here + is the <span class="new_code">LoggingSessionPool</span>. + In particular we would like to check that the + <span class="new_code">findSession()</span> method is + called with the correct session ID in the cookie and that + it sent the message "Starting session $cookie" + to the logger. + </p> + <p> + Despite the fact that we are testing only a few lines of + production code, here is what we would have to do in a + conventional test case: + <ol> + <li>Create a log object.</li> + <li>Set a directory to place the log file.</li> + <li>Set the directory permissions so we can write the log.</li> + <li>Create a <span class="new_code">SessionPool</span> object.</li> + <li>Hand start a session, which probably does lot's of things.</li> + <li>Invoke <span class="new_code">findSession()</span>.</li> + <li>Read the new Session ID (hope there is an accessor!).</li> + <li>Raise a test assertion to confirm that the ID matches the cookie.</li> + <li>Read the last line of the log file.</li> + <li>Pattern match out the extra logging timestamps, etc.</li> + <li>Assert that the session message is contained in the text.</li> + </ol> + It is hardly surprising that developers hate writing tests + when they are this much drudgery. + To make things worse, every time the logging format changes or + the method of creating new sessions changes, we have to rewrite + parts of this test even though this test does not officially + test those parts of the system. + We are creating headaches for the writers of these other classes. + </p> + <p> + Instead, here is the complete test method using mock object magic... +<pre> +Mock::generate('Session'); +Mock::generate('SessionPool'); +Mock::generate('Log'); + +class LoggingSessionPoolTest extends UnitTestCase { + ... + function testFindSessionLogging() {<strong> + $session = &new MockSession(); + $pool = &new MockSessionPool(); + $pool->setReturnReference('findSession', $session); + $pool->expectOnce('findSession', array('abc')); + + $log = &new MockLog(); + $log->expectOnce('message', array('Starting session abc')); + + $logging_pool = &new LoggingSessionPool($pool, $log); + $this->assertReference($logging_pool->findSession('abc'), $session);</strong> + } +} +</pre> + We start by creating a dummy session. + We don't have to be too fussy about this as the check + for which session we want is done elsewhere. + We only need to check that it was the same one that came + from the session pool. + </p> + <p> + <span class="new_code">findSession()</span> is a factory + method the simulation of which is described <a href="#stub">above</a>. + The point of departure comes with the first + <span class="new_code">expectOnce()</span> call. + This line states that whenever + <span class="new_code">findSession()</span> is invoked on the + mock, it will test the incoming arguments. + If it receives the single argument of a string "abc" + then a test pass is sent to the unit tester, otherwise a fail is + generated. + This was the part where we checked that the right session was asked for. + The argument list follows the same format as the one for setting + return values. + You can have wildcards and sequences and the order of + evaluation is the same. + </p> + <p> + We use the same pattern to set up the mock logger. + We tell it that it should have + <span class="new_code">message()</span> invoked + once only with the argument "Starting session abc". + By testing the calling arguments, rather than the logger output, + we insulate the test from any display changes in the logger. + </p> + <p> + We start to run our tests when we create the new + <span class="new_code">LoggingSessionPool</span> and feed + it our preset mock objects. + Everything is now under our control. + </p> + <p> + This is still quite a bit of test code, but the code is very + strict. + If it still seems rather daunting there is a lot less of it + than if we tried this without mocks and this particular test, + interactions rather than output, is always more work to set + up. + More often you will be testing more complex situations without + needing this level or precision. + Also some of this can be refactored into a test case + <span class="new_code">setUp()</span> method. + </p> + <p> + Here is the full list of expectations you can set on a mock object + in <a href="http://www.lastcraft.com/simple_test.php">SimpleTest</a>... + <table> +<thead> + <tr> +<th>Expectation</th> +<th>Needs <span class="new_code">tally()</span> +</th> +</tr> + </thead> +<tbody> +<tr> + <td><span class="new_code">expect($method, $args)</span></td> + <td style="text-align: center">No</td> + </tr> + <tr> + <td><span class="new_code">expectAt($timing, $method, $args)</span></td> + <td style="text-align: center">No</td> + </tr> + <tr> + <td><span class="new_code">expectCallCount($method, $count)</span></td> + <td style="text-align: center">Yes</td> + </tr> + <tr> + <td><span class="new_code">expectMaximumCallCount($method, $count)</span></td> + <td style="text-align: center">No</td> + </tr> + <tr> + <td><span class="new_code">expectMinimumCallCount($method, $count)</span></td> + <td style="text-align: center">Yes</td> + </tr> + <tr> + <td><span class="new_code">expectNever($method)</span></td> + <td style="text-align: center">No</td> + </tr> + <tr> + <td><span class="new_code">expectOnce($method, $args)</span></td> + <td style="text-align: center">Yes</td> + </tr> + <tr> + <td><span class="new_code">expectAtLeastOnce($method, $args)</span></td> + <td style="text-align: center">Yes</td> + </tr> + </tbody> +</table> + Where the parameters are... + <dl> + <dt class="new_code">$method</dt> + <dd>The method name, as a string, to apply the condition to.</dd> + <dt class="new_code">$args</dt> + <dd> + The arguments as a list. Wildcards can be included in the same + manner as for <span class="new_code">setReturn()</span>. + This argument is optional for <span class="new_code">expectOnce()</span> + and <span class="new_code">expectAtLeastOnce()</span>. + </dd> + <dt class="new_code">$timing</dt> + <dd> + The only point in time to test the condition. + The first call starts at zero. + </dd> + <dt class="new_code">$count</dt> + <dd>The number of calls expected.</dd> + </dl> + The method <span class="new_code">expectMaximumCallCount()</span> + is slightly different in that it will only ever generate a failure. + It is silent if the limit is never reached. + </p> + <p> + Also if you have juste one call in your test, make sure you're using + <span class="new_code">expectOnce</span>.<br> + Using <span class="new_code">$mocked->expectAt(0, 'method', 'args);</span> + on its own will not be catched : + checking the arguments and the overall call count + are currently independant. + </p> + <p> + Like the assertions within test cases, all of the expectations + can take a message override as an extra parameter. + Also the original failure message can be embedded in the output + as "%s". + </p> + + <p><a class="target" name="approaches"><h2>Other approaches</h2></a></p> + <p> + There are three approaches to creating mocks including the one + that SimpleTest employs. + Coding them by hand using a base class, generating them to + a file and dynamically generating them on the fly. + </p> + <p> + Mock objects generated with <a href="simple_test.html">SimpleTest</a> + are dynamic. + They are created at run time in memory, using + <span class="new_code">eval()</span>, rather than written + out to a file. + This makes the mocks easy to create, a one liner, + especially compared with hand + crafting them in a parallel class hierarchy. + The problem is that the behaviour is usually set up in the tests + themselves. + If the original objects change the mock versions + that the tests rely on can get out of sync. + This can happen with the parallel hierarchy approach as well, + but is far more quickly detected. + </p> + <p> + The solution, of course, is to add some real integration + tests. + You don't need very many and the convenience gained + from the mocks more than outweighs the small amount of + extra testing. + You cannot trust code that was only tested with mocks. + </p> + <p> + If you are still determined to build static libraries of mocks + because you want to simulate very specific behaviour, you can + achieve the same effect using the SimpleTest class generator. + In your library file, say <em>mocks/connection.php</em> for a + database connection, create a mock and inherit to override + special methods or add presets... +<pre> +<?php + require_once('simpletest/mock_objects.php'); + require_once('../classes/connection.php'); +<strong> + Mock::generate('Connection', 'BasicMockConnection'); + class MockConnection extends BasicMockConnection { + function MockConnection() { + $this->BasicMockConnection(); + $this->setReturn('query', false); + } + }</strong> +?> +</pre> + The generate call tells the class generator to create + a class called <span class="new_code">BasicMockConnection</span> + rather than the usual <span class="new_code">MockConnection</span>. + We then inherit from this to get our version of + <span class="new_code">MockConnection</span>. + By intercepting in this way we can add behaviour, here setting + the default value of <span class="new_code">query()</span> to be false. + By using the default name we make sure that the mock class + generator will not recreate a different one when invoked elsewhere in the + tests. + It never creates a class if it already exists. + As long as the above file is included first then all tests + that generated <span class="new_code">MockConnection</span> should + now be using our one instead. + If we don't get the order right and the mock library + creates one first then the class creation will simply fail. + </p> + <p> + Use this trick if you find you have a lot of common mock behaviour + or you are getting frequent integration problems at later + stages of testing. + </p> + + </div> + References and related information... + <ul> +<li> + The original + <a href="http://www.mockobjects.com/">Mock objects</a> paper. + </li> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest home page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <span class="chosen">Mock objects</span> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/overview.html b/vendors/simpletest/docs/en/overview.html new file mode 100644 index 000000000..5bed89e51 --- /dev/null +++ b/vendors/simpletest/docs/en/overview.html @@ -0,0 +1,486 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <span class="chosen">Overview</span> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Overview of SimpleTest</h1> + This page... + <ul> +<li> + <a href="#summary">Quick summary</a> + of the SimpleTest tool for PHP. + </li> +<li> + <a href="#features">List of features</a>, + both current ones and those planned. + </li> +<li> + There are plenty of <a href="#resources">unit testing resources</a> + on the web. + </li> +</ul> +<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 +require_once('simpletest/autorun.php'); + +class <strong>MyTestCase</strong> extends UnitTestCase { + <strong> + function testCreatedLogFile() { + $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 programming 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 +require_once('simpletest/autorun.php'); +require_once('simpletest/web_tester.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->assertPattern('/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 SVN directly. + <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;">1.0</td> + </tr> + <tr> + <td>Html display</td> + <td>Simplest possible display</td> + <td style="color: green;">1.0</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;">1.0</td> + </tr> + <tr> + <td>Mock objects</td> + <td> + Objects capable of simulating other objects removing + test dependencies + </td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Web test case</td> + <td>Allows link following and title tag matching</td> + <td style="color: green;">1.0</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;">1.0</td> + </tr> + <tr> + <td>Web cookie handling</td> + <td>Correct handling of cookies when fetching pages</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Following redirects</td> + <td>Page fetching automatically follows 300 redirects</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Form parsing</td> + <td>Ability to submit simple forms and read default form values</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Command line interface</td> + <td>Test display without the need of a web browser</td> + <td style="color: green;">1.0</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;">1.0</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;">1.0</td> + </tr> + <tr> + <td>Browser component</td> + <td> + Exposure of lower level web browser interface for more + detailed test cases + </td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>HTTP authentication</td> + <td> + Fetching protected web pages with basic authentication + only + </td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>SSL support</td> + <td>Can connect to https: pages</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Proxy support</td> + <td>Can connect via. common proxies</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>Frames support</td> + <td>Handling of frames in web test cases</td> + <td style="color: green;">1.0</td> + </tr> + <tr> + <td>File upload testing</td> + <td>Can simulate the input type file tag</td> + <td style="color: green;">1.0.1</td> + </tr> + <tr> + <td>Mocking interfaces</td> + <td> + Can generate mock objects to interfaces as well as classes + and class interfaces are carried for type hints + </td> + <td style="color: green;">1.0.1</td> + </tr> + <tr> + <td>Testing exceptions</td> + <td>Similar to testing PHP errors</td> + <td style="color: green;">1.0.1</td> + </tr> + <tr> + <td>HTML label support</td> + <td>Can access all controls using the visual label</td> + <td style="color: green;">1.0.1</td> + </tr> + <tr> + <td>Base tag support</td> + <td>Respects page base tag when clicking</td> + <td style="color: green;">1.0.1</td> + </tr> + <tr> + <td>PHP 5 E_STRICT compliant</td> + <td>PHP 5 only version that works with the E_STRICT error level</td> + <td style="color: red;">1.1</td> + </tr> + <tr> + <td>BDD style fixtures</td> + <td>Can import fixtures using a mixin like given() method</td> + <td style="color: red;">1.5</td> + </tr> + <tr> + <td>Reporting machinery enhancements</td> + <td>Improved message passing for better cooperation with IDEs</td> + <td style="color: red;">1.5</td> + </tr> + <tr> + <td>Fluent mock interface</td> + <td>More flexible and concise mock objects</td> + <td style="color: red;">1.6</td> + </tr> + <tr> + <td>Localisation</td> + <td>Messages abstracted and code generated</td> + <td style="color: red;">1.6</td> + </tr> + <tr> + <td>CSS selectors</td> + <td>HTML content can be examined using CSS selectors</td> + <td style="color: red;">1.7</td> + </tr> + <tr> + <td>HTML table assertions</td> + <td>Can match HTML or table elements to expectations</td> + <td style="color: red;">1.7</td> + </tr> + <tr> + <td>Unified acceptance testing model</td> + <td>Content searchable through selectors combined with expectations</td> + <td style="color: red;">1.7</td> + </tr> + <tr> + <td>DatabaseTestCase</td> + <td>SQL selectors and DB drivers</td> + <td style="color: red;">1.7</td> + </tr> + <tr> + <td>IFrame support</td> + <td>Reads IFrame content that can be refreshed</td> + <td style="color: red;">1.8</td> + </tr> + <tr> + <td>Alternate HTML parsers</td> + <td>Can detect compiled parsers for performance improvements</td> + <td style="color: red;">1.8</td> + </tr> + <tr> + <td>Integrated Selenium support</td> + <td>Easy to use built in Selenium driver and tutorial</td> + <td style="color: red;">1.9</td> + </tr> + <tr> + <td>Code coverage</td> + <td>Reports using the bundled tool when using XDebug</td> + <td style="color: red;">1.9</td> + </tr> + <tr> + <td>Deprecation of old methods</td> + <td>Simpler interface for SimpleTest2</td> + <td style="color: red;">2.0</td> + </tr> + <tr> + <td>Javascript suport</td> + <td>Use of PECL module to add Javascript to the native browser</td> + <td style="color: red;">3.0</td> + </tr> + </tbody> +</table> + PHP5 migraton will start straight after the version 1.0.1 series, + whereupon only PHP 5.1+ will be supported. + SimpleTest is currently compatible with PHP 5, but will not + make use of all of the new features until version 1.1. + </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 you can find pretty complete + lists from + <a href="http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks">Wikipedia</a>, + <a href="http://www.testingfaqs.org/t-unit.html">Software testing FAQ</a>, + and <a href="http://www.opensourcetesting.org/functional.php">Open source testing</a>. + </p> + <p> + There is still very little material on using 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. + The most authoritive sources are probably + <a href="http://mockobjects.com">the original mock objects site</a> and + <a href="http://jmock.org/">JMock</a>. + Java centric, but tucked away in PDFs they contain some deep knowledge on using mocks from the + extended experience of the concept inventors. + 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/MocksObjectsPaper.html">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 the scriptable ones + are mostly 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. + </p> + <p> + Most significant is a new generation of tools that run directly in the web browser + are now available. + These include + <a href="http://www.openqa.org/selenium/">Selenium</a> and + <a href="http://wtr.rubyforge.org/">Watir</a>. + They are non-trivial to set up and slow to run, but can essentially test anything. + As SimpleTest does not support JavaScript you would probably + have to look at these tools anyway if you have highly dynamic + pages. + </p> + + </div> + References and related information... + <ul> +<li> + <a href="unit_test_documentation.html">Documentation for SimpleTest</a>. + </li> +<li> + <a href="http://www.lastcraft.com/first_test_tutorial.php">How to write PHP test cases</a> + is a fairly advanced tutorial. + </li> +<li> + <a href="http://simpletest.org/api/">SimpleTest API</a> from phpdoc. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <span class="chosen">Overview</span> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/partial_mocks_documentation.html b/vendors/simpletest/docs/en/partial_mocks_documentation.html new file mode 100644 index 000000000..4cae18c9d --- /dev/null +++ b/vendors/simpletest/docs/en/partial_mocks_documentation.html @@ -0,0 +1,445 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <span class="chosen">Partial mocks</span> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Partial mock objects documentation</h1> + This page... + <ul> +<li> + <a href="#inject">The mock injection problem</a>. + </li> +<li> + Moving creation to a <a href="#creation">protected factory</a> method. + </li> +<li> + <a href="#partial">Partial mocks</a> generate subclasses. + </li> +<li> + Partial mocks <a href="#less">test less than a class</a>. + </li> +</ul> +<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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + <a href="http://simpletest.org/api/">Full API for SimpleTest</a> + from the PHPDoc. + </li> +<li> + The protected factory is described in + <a href="http://www-106.ibm.com/developerworks/java/library/j-mocktest.html">this paper from IBM</a>. + This is the only formal comment I have seen on this problem. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <span class="chosen">Partial mocks</span> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/reporter_documentation.html b/vendors/simpletest/docs/en/reporter_documentation.html new file mode 100644 index 000000000..87c89e44b --- /dev/null +++ b/vendors/simpletest/docs/en/reporter_documentation.html @@ -0,0 +1,519 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <span class="chosen">Reporting</span> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Test reporter documentation</h1> + This page... + <ul> +<li> + Displaying <a href="#html">results in HTML</a> + </li> +<li> + Displaying and <a href="#other">reporting results</a> + in other formats + </li> +<li> + Using <a href="#cli">SimpleTest from the command line</a> + </li> +<li> + Using <a href="#xml">Using XML</a> for remote testing + </li> +</ul> +<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 TestSuite('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 TestSuite('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 TestSuite('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 TestSuite('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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <span class="chosen">Reporting</span> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/unit_test_documentation.html b/vendors/simpletest/docs/en/unit_test_documentation.html new file mode 100644 index 000000000..bc43c82cc --- /dev/null +++ b/vendors/simpletest/docs/en/unit_test_documentation.html @@ -0,0 +1,431 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <span class="chosen">Unit tester</span> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>PHP Unit Test documentation</h1> + This page... + <ul> +<li> + <a href="#unit">Unit test cases</a> and basic assertions. + </li> +<li> + <a href="#extending_unit">Extending test cases</a> to + customise them for your own project. + </li> +<li> + <a href="#running_unit">Running a single case</a> as + a single script. + </li> +</ul> +<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> + 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. + </p> + <p> + For example... +<pre> +require_once('simpletest/autorun.php'); +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">assertWithinMargin($x, $y, $m)</span></td> +<td>Fail if abs($x - $y) < $m is false</td> +</tr> + <tr> +<td><span class="new_code">assertOutsideMargin($x, $y, $m)</span></td> +<td>Fail if abs($x - $y) < $m 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">assertClone($x, $y)</span></td> +<td>Fail unless $x and $y are identical copies</td> +</tr> + <tr> +<td><span class="new_code">assertPattern($p, $x)</span></td> +<td>Fail unless the regex $p matches $x</td> +</tr> + <tr> +<td><span class="new_code">assertNoPattern($p, $x)</span></td> +<td>Fail if the regex $p matches $x</td> +</tr> + <tr> +<td><span class="new_code">expectError($x)</span></td> +<td>Swallows any upcoming matching error</td> +</tr> + <tr> +<td><span class="new_code">assert($e)</span></td> +<td>Fail on failed <a href="expectation_documentation.html">expectation</a> object $e</td> +</tr> + </tbody></table> + All assertion methods can take an optional description as a + last parameter. + This is 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> +$variable = null; +<strong>$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. +<pre> +$a = 1; +$b = $a; +<strong>$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->assertPattern('/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>$this->expectError();</strong> +trigger_error('Catastrophe'); +</pre> + Here the check catches the "Catastrophe" + message without checking the text and passes. + This removes the error from the queue. +<pre> +<strong>$this->expectError('Catastrophe');</strong> +trigger_error('Catastrophe'); +</pre> + The next error check tests not only the existence of the error, + but also the text which, here matches so another pass. + If any unchecked errors are left at the end of a test method then + an exception will be reported in the test. + </p> + <p> + 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">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> + </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, so as to extend framework... +<pre> +require_once('simpletest/autorun.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> + To prevent this test case being run accidently, it is + advisable to mark it as <span class="new_code">abstract</span>. + </p> + <p> + Alternatively you could add a + <span class="new_code">SimpleTestOptions::ignore('FileTester');</span> + directive in your code. + </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 a few basics, + 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. + With <em>autorun</em> no particular scaffolding is needed, + just launch your particular test file and you're ready to go. + </p> + <p> + You can even decide which reporter (for example, + <span class="new_code">TextReporter</span> or <span class="new_code">HtmlReporter</span>) + you prefer for a specific file when launched on its own... +<pre> +<?php +require_once('simpletest/autorun.php');<strong> +SimpleTest :: prefer(new TextReporter());</strong> +require_once('../classes/writer.php'); + +class FileTestCase extends UnitTestCase { + ... +} +?> +</pre> + This script will run as is, but of course will output zero passes + and zero failures until test methods are added. + </p> + + </div> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + <a href="http://simpletest.org/api/">Full API for SimpleTest</a> + from the PHPDoc. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <span class="chosen">Unit tester</span> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <a href="web_tester_documentation.html">Web tester</a> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> diff --git a/vendors/simpletest/docs/en/web_tester_documentation.html b/vendors/simpletest/docs/en/web_tester_documentation.html new file mode 100644 index 000000000..2602dd56d --- /dev/null +++ b/vendors/simpletest/docs/en/web_tester_documentation.html @@ -0,0 +1,584 @@ +<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"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <span class="chosen">Web tester</span> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<h1>Web tester documentation</h1> + This page... + <ul> +<li> + Successfully <a href="#fetch">fetching a web page</a> + </li> +<li> + Testing the <a href="#content">page content</a> + </li> +<li> + <a href="#navigation">Navigating a web site</a> + while testing + </li> +<li> + <a href="#request">Raw request modifications</a> and debugging methods + </li> +</ul> +<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 +require_once('simpletest/autorun.php');<strong> +require_once('simpletest/web_tester.php');</strong> +SimpleTest::prefer(new TextReporter()); + +class WebTests extends TestSuite { + function WebTests() { + $this->TestSuite('Web site tests');<strong> + $this->addFile('lastcraft_test.php');</strong> + } +} +?> +</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->assertText('Why the last craft');</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">assertText($text)</span></td> +<td>Pass if matches visible and "alt" text</td> +</tr> + <tr> +<td><span class="new_code">assertNoText($text)</span></td> +<td>Pass if doesn't match visible and "alt" text</td> +</tr> + <tr> +<td><span class="new_code">assertPattern($pattern)</span></td> +<td>A Perl pattern match against the page content</td> +</tr> + <tr> +<td><span class="new_code">assertNoPattern($pattern)</span></td> +<td>A Perl pattern match to not find content</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">assertNoHeader($header)</span></td> +<td>Pass if a header was not fetched</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> + ...or, if that is too long and fragile... +<pre> +<strong>$this->assertTitle(new PatternExpectation('/The Last Craft/'));</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(new PatternExpectation('/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> can be used + with the button title... +<pre> +<strong>$this->clickSubmit('Go!');</strong> +</pre> + If you are not sure or don't care, the usual case, then just + use the <span class="new_code">click()</span> method... +<pre> +<strong>$this->click('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> + References and related information... + <ul> +<li> + SimpleTest project page on <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + SimpleTest download page on <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>. + </li> +<li> + The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a> + gives full detail on the classes and assertions available. + </li> +</ul> +<div class="menu_back"><div class="menu"> +<a href="index.html">SimpleTest</a> + | + <a href="overview.html">Overview</a> + | + <a href="unit_test_documentation.html">Unit tester</a> + | + <a href="group_test_documentation.html">Group tests</a> + | + <a href="mock_objects_documentation.html">Mock objects</a> + | + <a href="partial_mocks_documentation.html">Partial mocks</a> + | + <a href="reporter_documentation.html">Reporting</a> + | + <a href="expectation_documentation.html">Expectations</a> + | + <span class="chosen">Web tester</span> + | + <a href="form_testing_documentation.html">Testing forms</a> + | + <a href="authentication_documentation.html">Authentication</a> + | + <a href="browser_documentation.html">Scriptable browser</a> +</div></div> +<div class="copyright"> + Copyright<br>Marcus Baker 2006 + </div> +</body> +</html> |