feat: add videodb media index with Docker stack
- Add videodb PHP/MySQL media collection manager (Blu-ray, DVD, CD) - Dockerfile: PHP 8.1 + Apache with GD/mysqli/exif extensions - docker-compose.yml: app on port 6761 + MySQL 8.0 with health checks - docker-entrypoint.sh: auto-generates config.inc.php from env vars, waits for MySQL, initializes DB schema idempotently - init-db.php: CLI schema installer using app's own prefix_query() logic - Persistent volumes for DB, cache, and cover images Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="basic"></a>Basic HTTP authentication</h2>
|
||||
<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>
|
||||
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>
|
||||
This type of test, testing HTTP responses, is not typical.
|
||||
</p>
|
||||
<p>
|
||||
Most of the time we are not interested in testing the
|
||||
authentication itself, but want to get past it to test
|
||||
the pages underneath.
|
||||
As soon as the challenge has been issued we can reply with
|
||||
an authentication response...
|
||||
<pre>
|
||||
class AuthenticationTest extends WebTestCase {
|
||||
function 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>
|
||||
If you want, 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.
|
||||
I'm afraid we leave this up to you.
|
||||
</p>
|
||||
<p>
|
||||
A problem with encoding the login details directly in the URL is
|
||||
the authentication header will not be sent on subsequent requests.
|
||||
If you navigate with relative URLs though, the authentication
|
||||
information will be preserved along with the domain name.
|
||||
</p>
|
||||
<p>
|
||||
Normally though, you use the <span class="new_code">authenticate()</span> call.
|
||||
SimpleTest will then remember your login information on each request.
|
||||
</p>
|
||||
<p>
|
||||
Only testing with basic authentication is currently supported, and
|
||||
this is only really secure in tandem with HTTPS connections.
|
||||
This is usually good enough to protect test server from prying eyes,
|
||||
however.
|
||||
Digest authentication and NTLM authentication may be added
|
||||
in the future if enough people request this feature.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="cookies"></a>Cookies</h2>
|
||||
<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 with complicated timeouts.
|
||||
We need to be able to test this too.
|
||||
</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>
|
||||
If you are using PHP to handle sessions for you then
|
||||
this test is even more useless, as we are just testing PHP itself.
|
||||
</p>
|
||||
<p>
|
||||
The simplest test of logging in is to visually inspect the
|
||||
next page to see if you are really logged in.
|
||||
Just test the next page with <span class="new_code">WebTestCase::assertText()</span>.
|
||||
</p>
|
||||
<p>
|
||||
The test is similar to any other form test,
|
||||
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 and we haven't accidently reset it.
|
||||
</p>
|
||||
<p>
|
||||
We could even attempt to hack 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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="session"></a>Browser sessions</h2>
|
||||
<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 waiting
|
||||
for the cookie to pass 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>
|
||||
501
videodb/test/simpletest/docs/en/browser_documentation.html
Normal file
501
videodb/test/simpletest/docs/en/browser_documentation.html
Normal file
@@ -0,0 +1,501 @@
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="scripting"></a>The Scriptable Browser</h2>
|
||||
<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.
|
||||
<p>
|
||||
</p>
|
||||
Methods such as periodic scraping 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">click($label)</span></td>
|
||||
<td>Clicks visible link or button text</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($label, $value)</span></td>
|
||||
<td>Sets all form fields with that label or name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">setFieldByName($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($label)</span></td>
|
||||
<td>Accessor for a form element value by label tag and then name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">getFieldByName($name)</span></td>
|
||||
<td>Accessor for a form element value using name attribute</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 many methods to list available links and fields.
|
||||
<table><tbody>
|
||||
<tr>
|
||||
<td><span class="new_code">isClickable($label)</span></td>
|
||||
<td>Test to see if a click target exists by label or name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">isSubmit($label)</span></td>
|
||||
<td>Test for the existence of a button with that label or name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">isImage($label)</span></td>
|
||||
<td>Test for the existence of an image button with that label or name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">getLink($label)</span></td>
|
||||
<td>Finds a URL from its label</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">getLinkById($label)</span></td>
|
||||
<td>Finds a URL from its ID attribute</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">getUrls()</span></td>
|
||||
<td>Lists available links in the current page</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
This will be expanded in later versions of SimpleTest.
|
||||
</p>
|
||||
<p>
|
||||
Frames are a rather esoteric feature these days, but SimpleTest has
|
||||
retained support for them.
|
||||
</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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="debug"></a>What went wrong?</h2>
|
||||
<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">getUrl()</span></td>
|
||||
<td>Url of most recent page fetched</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">getBaseUrl()</span></td>
|
||||
<td>Base url only of most recent page fetched</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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="unit"></a>Complex unit tests with multiple browsers</h2>
|
||||
<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 could 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_attempt = new SimpleBrowser();
|
||||
$first_attempt->get('http://my-site.com/login.php');
|
||||
$first_attempt->setField('name', 'Me');
|
||||
$first_attempt->setField('password', 'Secret');
|
||||
$first_attempt->click('Enter');
|
||||
$this->assertEqual($first_attempt->getTitle(), 'Welcome');
|
||||
|
||||
$second_attempt = new SimpleBrowser();
|
||||
$second_attempt->get('http://my-site.com/login.php');
|
||||
$second_attempt->setField('name', 'Me');
|
||||
$second_attempt->setField('password', 'Secret');
|
||||
$second_attempt->click('Enter');
|
||||
$this->assertEqual($second_attempt->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, such as PHPUnit.
|
||||
</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>
|
||||
121
videodb/test/simpletest/docs/en/docs.css
Normal file
121
videodb/test/simpletest/docs/en/docs.css
Normal file
@@ -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;
|
||||
}
|
||||
476
videodb/test/simpletest/docs/en/expectation_documentation.html
Normal file
476
videodb/test/simpletest/docs/en/expectation_documentation.html
Normal file
@@ -0,0 +1,476 @@
|
||||
<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">
|
||||
<h2>
|
||||
<a class="target" name="mock"></a>More control over mock objects</h2>
|
||||
<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">PatternExpectation</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">PatternExpectation</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>
|
||||
<tr>
|
||||
<td><span class="new_code">TrueExpectation</span></td>
|
||||
<td>Accepts any PHP variable that evaluates to true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">FalseExpectation</span></td>
|
||||
<td>Accepts any PHP variable that evaluates to false</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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="behaviour"></a>Using expectations to control stubs</h2>
|
||||
<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->returns(
|
||||
'isAllowed',
|
||||
true,
|
||||
array(new IsAExpectation('Session', 'Must be a session')));
|
||||
$authorisation->returns('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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="extending"></a>Creating your own expectations</h2>
|
||||
<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> or further extend a subclass...
|
||||
<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>
|
||||
Here is a more typical example, matching part of a hash...
|
||||
<pre>
|
||||
<strong>class JustField extends EqualExpectation {
|
||||
private $key;
|
||||
|
||||
function __construct($key, $expected) {
|
||||
parent::__construct($expected);
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
function test($compare) {
|
||||
if (! isset($compare[$this->key])) {
|
||||
return false;
|
||||
}
|
||||
return parent::test($compare[$this->key]);
|
||||
}
|
||||
|
||||
function testMessage($compare) {
|
||||
if (! isset($compare[$this->key])) {
|
||||
return 'Key [' . $this->key . '] does not exist';
|
||||
}
|
||||
return 'Key [' . $this->key . '] -> ' .
|
||||
parent::testMessage($compare[$this->key]);
|
||||
}
|
||||
}</strong>
|
||||
</pre>
|
||||
We tend to seperate message clauses with
|
||||
"&nbsp;->&nbsp;".
|
||||
This allows derivative tools to reformat the output.
|
||||
</p>
|
||||
<p>
|
||||
Suppose some authenticator is expecting to be given
|
||||
a database row corresponding to the user, and we
|
||||
only need to confirm the username is correct.
|
||||
We can assert just their username with...
|
||||
<pre>
|
||||
$mock->expectOnce('authenticate',
|
||||
array(new JustKey('username', 'marcus')));
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="unit"></a>Under the bonnet of the unit tester</h2>
|
||||
<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 generic
|
||||
<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>
|
||||
<span class="new_code">assert()</span> will test any expectation class directly.
|
||||
</p>
|
||||
<p>
|
||||
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 rare to need the expectations for more than pattern
|
||||
matching, but these facilities do allow testers to build
|
||||
some sort of domain language for testing their application.
|
||||
Also, complex expectation classes could make the tests
|
||||
harder to read and debug.
|
||||
In effect extending 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>
|
||||
351
videodb/test/simpletest/docs/en/form_testing_documentation.html
Normal file
351
videodb/test/simpletest/docs/en/form_testing_documentation.html
Normal file
@@ -0,0 +1,351 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>SimpleTest 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">
|
||||
<h2>
|
||||
<a class="target" name="submit"></a>Submitting a simple form</h2>
|
||||
<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 of a fixed
|
||||
string.
|
||||
<pre>
|
||||
class SimpleFormTests extends WebTestCase {
|
||||
function testDefaultValue() {
|
||||
$this->get('http://www.lastcraft.com/form_testing_documentation.php');
|
||||
$this->assertField('a', <strong>new PatternExpectation('/default/')</strong>);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
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.
|
||||
In general SimpleTest tries to emulate typical browser behaviour as much as possible,
|
||||
rather than attempting to catch any form of HTML omission.
|
||||
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,
|
||||
or any of the HTML and CSS validators already out there.
|
||||
</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 current selection will not be changed if the new value is not an option.
|
||||
</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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="multiple"></a>Fields with multiple values</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="hidden-field"></a>Forms which use javascript to set a hidden field</h2>
|
||||
<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 testEmulateMyJavascriptForm() {
|
||||
<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() {
|
||||
<strong>$this->clickSubmit('OK', array('a_hidden_field'=>'123'));</strong>
|
||||
}
|
||||
|
||||
}
|
||||
</pre>
|
||||
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
|
||||
test.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="raw"></a>Raw posting</h2>
|
||||
<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 emulating a form submission.
|
||||
You would normally only do this as a temporary expedient, or where
|
||||
you are expecting a 3rd party to submit to a form.
|
||||
The exception is when you want tests to protect you from
|
||||
attempts to spoof your pages.
|
||||
</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>
|
||||
252
videodb/test/simpletest/docs/en/group_test_documentation.html
Normal file
252
videodb/test/simpletest/docs/en/group_test_documentation.html
Normal file
@@ -0,0 +1,252 @@
|
||||
<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>
|
||||
</ul>
|
||||
<div class="content">
|
||||
<h2>
|
||||
<a class="target" name="group"></a>Grouping tests into suites</h2>
|
||||
<p>
|
||||
There are many ways to group tests together into test suites.
|
||||
One way is to simply place multiple test cases into a single file...
|
||||
<pre>
|
||||
<strong><?php
|
||||
require_once(dirname(__FILE__) . '/simpletest/autorun.php');
|
||||
require_once(dirname(__FILE__) . '/../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 need none of the SimpleTest libraries.
|
||||
</p>
|
||||
<p>
|
||||
Occasionally special subclasses are created that methods useful
|
||||
for testing part of the application.
|
||||
These new base classes are then used in place of <span class="new_code">UnitTestCase</span>
|
||||
or <span class="new_code">WebTestCase</span>.
|
||||
You don't normally want to run these as test cases.
|
||||
Simply mark any base 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>
|
||||
Here the <span class="new_code">FileTester</span> class does
|
||||
not contain any actual tests, but is the base class for other
|
||||
test cases.
|
||||
</p>
|
||||
<p>
|
||||
We will call this sample <em>file_test.php</em>.
|
||||
Currently the test cases are grouped simply by being in the same file.
|
||||
We can build larger constructs just by including other test files in.
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
require_once('file_test.php');
|
||||
?>
|
||||
</pre>
|
||||
This will work, but create a purely flat hierarchy.
|
||||
INstead we create a test suite file.
|
||||
Our top level test suite can look like this...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
class AllFileTests extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
<strong>$this->addFile('file_test.php');</strong>
|
||||
}
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
What happens here is that the <span class="new_code">TestSuite</span>
|
||||
class will do 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 composes
|
||||
them to the test suite.
|
||||
This method gives us the most control as we just manually add
|
||||
more test files as our test suite grows.
|
||||
</p>
|
||||
<p>
|
||||
If this is too much typing, and you are willing to group
|
||||
test suites together in their own directories or otherwise
|
||||
tag the file names, then there is a more automatic way...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
class AllFileTests extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->collect(dirname(__FILE__) . '/unit',
|
||||
new SimplePatternCollector('/_test.php/'));
|
||||
}
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
This will scan a directory called "unit" for any files
|
||||
ending with "_test.php" and load them.
|
||||
You don't have to use <span class="new_code">SimplePatternCollector</span> to
|
||||
filter by a pattern in the filename, but this is the most common
|
||||
usage.
|
||||
</p>
|
||||
<p>
|
||||
That snippet above is very common in practice.
|
||||
Now all you have to do is drop a file of test cases into the
|
||||
directory and it will run just by running the test suite script.
|
||||
</p>
|
||||
<p>
|
||||
The catch is that you cannot control the order in which the test
|
||||
cases are run.
|
||||
If you want to see lower level components fail first in the test suite,
|
||||
and this will make diagnosis a lot easier, then you should manually
|
||||
call <span class="new_code">addFile()</span> for these.
|
||||
Tests cases are only loaded once, so it's fine to have these included
|
||||
again by a directory scan.
|
||||
</p>
|
||||
<p>
|
||||
Test cases loaded with the <span class="new_code">addFile</span> method have some
|
||||
useful properties.
|
||||
You can guarantee that the constructor is run
|
||||
just before the first test method and the destructor
|
||||
is run just after the last test method.
|
||||
This allows you to place test case wide set up and tear down
|
||||
code in the constructor and destructor, just like a normal
|
||||
class.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="higher"></a>Composite suites</h2>
|
||||
<p>
|
||||
The above method places all of the test cases into one large suite.
|
||||
For larger projects though this may not be flexible enough; you
|
||||
may want to group the tests together in all sorts of ways.
|
||||
</p>
|
||||
<p>
|
||||
Everything we have described so far with test scripts applies to
|
||||
<span class="new_code">TestSuite</span>s as well...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
<strong>
|
||||
class BigTestSuite extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addFile('file_tests.php');
|
||||
}
|
||||
}</strong>
|
||||
?>
|
||||
</pre>
|
||||
This effectively adds our test cases and a single suite below
|
||||
the first.
|
||||
When a test fails, we see the breadcrumb trail of the nesting.
|
||||
We can even mix groups and test cases freely as long as
|
||||
we are careful about loops in our includes.
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
class BigTestSuite extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addFile('file_tests.php');
|
||||
<strong>$this->addFile('some_other_test.php');</strong>
|
||||
}
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
Note that in the event of a double include, ony the first instance
|
||||
of the test case will be run.
|
||||
</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>
|
||||
542
videodb/test/simpletest/docs/en/index.html
Normal file
542
videodb/test/simpletest/docs/en/index.html
Normal file
@@ -0,0 +1,542 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>
|
||||
Download the SimpleTest 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>SimpleTest 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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="unit"></a>Using the tester quickly</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="group"></a>Building test suites</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="mock"></a>Using mock objects</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="web"></a>Web page testing</h2>
|
||||
<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 SimpleTest</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>
|
||||
870
videodb/test/simpletest/docs/en/mock_objects_documentation.html
Normal file
870
videodb/test/simpletest/docs/en/mock_objects_documentation.html
Normal file
@@ -0,0 +1,870 @@
|
||||
<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="#expectations">Mocks as critics</a> with expectations.
|
||||
</li>
|
||||
</ul>
|
||||
<div class="content">
|
||||
<h2>
|
||||
<a class="target" name="what"></a>What are mock objects?</h2>
|
||||
<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 (<a href="">Testing
|
||||
object-oriented systems</a>: 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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="creation"></a>Creating mock objects</h2>
|
||||
<p>
|
||||
All we need is an existing class or interface, say a database connection
|
||||
that looks like this...
|
||||
<pre>
|
||||
<strong>class DatabaseConnection {
|
||||
function DatabaseConnection() { }
|
||||
function query($sql) { }
|
||||
function selectQuery($sql) { }
|
||||
}</strong>
|
||||
</pre>
|
||||
To create a mock version of the class we need to run a
|
||||
code generator...
|
||||
<pre>
|
||||
require_once('simpletest/autorun.php');
|
||||
require_once('database_connection.php');
|
||||
|
||||
<strong>Mock::generate('DatabaseConnection');</strong>
|
||||
</pre>
|
||||
This code generates a clone class called
|
||||
<span class="new_code">MockDatabaseConnection</span>.
|
||||
This new class appears to be the same, but actually has no behaviour at all.
|
||||
</p>
|
||||
<p>
|
||||
The new class is usually a subclass of <span class="new_code">DatabaseConnection</span>.
|
||||
Unfortunately, there is no way to create a mock version of a
|
||||
class with a <span class="new_code">final</span> method without having a living version of
|
||||
that method.
|
||||
We consider that unsafe.
|
||||
If the target is an interface, or if <span class="new_code">final</span> methods are
|
||||
present in a target class, then a whole new class
|
||||
is created, but one implemeting the same interfaces.
|
||||
If you try to pass this separate class through a type hint that specifies
|
||||
the old concrete class name, it will fail.
|
||||
Code like that insists on type hinting to a class with <span class="new_code">final</span>
|
||||
methods probably cannot be safely tested with mocks.
|
||||
</p>
|
||||
<p>
|
||||
If you want to see the generated code, then simply <span class="new_code">print</span>
|
||||
the output of <span class="new_code">Mock::generate()</span>.
|
||||
Here is the generated code for the <span class="new_code">DatabaseConnection</span>
|
||||
class rather than the interface version...
|
||||
<pre>
|
||||
class MockDatabaseConnection extends DatabaseConnection {
|
||||
public $mock;
|
||||
protected $mocked_methods = array('databaseconnection', 'query', 'selectquery');
|
||||
|
||||
function MockDatabaseConnection() {
|
||||
$this->mock = new SimpleMock();
|
||||
$this->mock->disableExpectationNameChecks();
|
||||
}
|
||||
...
|
||||
function DatabaseConnection() {
|
||||
$args = func_get_args();
|
||||
$result = &$this->mock->invoke("DatabaseConnection", $args);
|
||||
return $result;
|
||||
}
|
||||
function query($sql) {
|
||||
$args = func_get_args();
|
||||
$result = &$this->mock->invoke("query", $args);
|
||||
return $result;
|
||||
}
|
||||
function selectQuery($sql) {
|
||||
$args = func_get_args();
|
||||
$result = &$this->mock->invoke("selectQuery", $args);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
Your output may vary depending on the exact version
|
||||
of SimpleTest you are using.
|
||||
</p>
|
||||
<p>
|
||||
Besides the original methods of the class, you will see some extra
|
||||
methods that help testing.
|
||||
More on these later.
|
||||
</p>
|
||||
<p>
|
||||
We can now create instances of the new class within
|
||||
our test case...
|
||||
<pre>
|
||||
require_once('simpletest/autorun.php');
|
||||
require_once('database_connection.php');
|
||||
|
||||
Mock::generate('DatabaseConnection');
|
||||
|
||||
class MyTestCase extends UnitTestCase {
|
||||
|
||||
function testSomething() {
|
||||
<strong>$connection = new MockDatabaseConnection();</strong>
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
The mock version now has all the methods of the original.
|
||||
Also, any type hints will be faithfully preserved.
|
||||
Say our query methods expect a <span class="new_code">Query</span> object...
|
||||
<pre>
|
||||
<strong>class DatabaseConnection {
|
||||
function DatabaseConnection() { }
|
||||
function query(Query $query) { }
|
||||
function selectQuery(Query $query) { }
|
||||
}</strong>
|
||||
</pre>
|
||||
If we now pass the wrong type of object, or worse a non-object...
|
||||
<pre>
|
||||
class MyTestCase extends UnitTestCase {
|
||||
|
||||
function testSomething() {
|
||||
$connection = new MockDatabaseConnection();
|
||||
$connection->query('insert into accounts () values ()');
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
...the code will throw a type violation at you just as the
|
||||
original class would.
|
||||
</p>
|
||||
<p>
|
||||
The mock version now has all the methods of the original.
|
||||
Unfortunately, they all return <span class="new_code">null</span>.
|
||||
As methods that always return <span class="new_code">null</span> are not that useful,
|
||||
we need to be able to set them to something else...
|
||||
</p>
|
||||
<p>
|
||||
<a class="target" name="stub"><h2>Mocks as actors</h2></a>
|
||||
</p>
|
||||
<p>
|
||||
Changing the return value of a method from <span class="new_code">null</span>
|
||||
to something else is pretty easy...
|
||||
<pre>
|
||||
<strong>$connection->returns('query', 37)</strong>
|
||||
</pre>
|
||||
Now every time we call
|
||||
<span class="new_code">$connection->query()</span> we get
|
||||
the result of 37.
|
||||
There is nothing special about 37.
|
||||
The return value can be arbitrarily complicated.
|
||||
</p>
|
||||
<p>
|
||||
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>
|
||||
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->returns('next', false);
|
||||
$iterator->returnsAt(0, 'next', 'First string');
|
||||
$iterator->returnsAt(1, 'next', 'Second string');</strong>
|
||||
...
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
When <span class="new_code">next()</span> is called on the
|
||||
<span class="new_code">MockIterator</span> 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 get($key) { ... }
|
||||
}
|
||||
</pre>
|
||||
This is a likely situation for using mock objects, as
|
||||
actual configuration will vary from machine to machine and
|
||||
even from test to test.
|
||||
The problem though is that all the data comes through the
|
||||
<span class="new_code">get()</span> method and yet
|
||||
we want different results for different keys.
|
||||
Luckily the mocks have a filter system...
|
||||
<pre>
|
||||
<strong>$config = &new MockConfiguration();
|
||||
$config->returns('get', 'primary', array('db_host'));
|
||||
$config->returns('get', 'admin', array('db_user'));
|
||||
$config->returns('get', '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">get()</span> method invoked
|
||||
like this...
|
||||
<pre>
|
||||
$config->get('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->returns('get', false, array('*'));</strong>
|
||||
</pre>
|
||||
This is not the same as setting the return value without
|
||||
any argument requirements like this...
|
||||
<pre><strong>
|
||||
$config->returns('get', 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 reference to be
|
||||
dished out by the mock rather than a copy or object handle.
|
||||
This a rarity to say the least, but you might be simulating
|
||||
a container that can hold primitives such as strings.
|
||||
For example...
|
||||
<pre>
|
||||
class Pad {
|
||||
function Pad() { }
|
||||
function &note($index) { }
|
||||
}
|
||||
</pre>
|
||||
In this case you can set a reference into the mock's
|
||||
return list...
|
||||
<pre>
|
||||
function testTaskReads() {
|
||||
$note = 'Buy books';
|
||||
$pad = new MockPad();
|
||||
$vector-><strong>returnsByReference(</strong>'note', $note, array(3)<strong>)</strong>;
|
||||
$task = new Task($pad);
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
With this arrangement you know that every time
|
||||
<span class="new_code">$pad->note(3)</span> is
|
||||
called it will return the same <span class="new_code">$note</span> each time,
|
||||
even if it get's modified.
|
||||
</p>
|
||||
<p>
|
||||
These three factors, timing, parameters and whether to copy,
|
||||
can be combined orthogonally.
|
||||
For example...
|
||||
<pre>
|
||||
$buy_books = 'Buy books';
|
||||
$write_code = 'Write code';
|
||||
$pad = new MockPad();
|
||||
$vector-><strong>returnsByReferenceAt(0, 'note', $buy_books, array('*', 3));</strong>
|
||||
$vector-><strong>returnsByReferenceAt(1, 'note', $write_code, array('*', 3));</strong>
|
||||
</pre>
|
||||
This will return a reference to <span class="new_code">$buy_books</span> and
|
||||
then to <span class="new_code">$write_code</span>, but only if two parameters
|
||||
were set the second of which must be the integer 3.
|
||||
That should cover most 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 the the iterator's <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...
|
||||
<pre>
|
||||
Mock::generate('DatabaseConnection');
|
||||
Mock::generate('ResultIterator');
|
||||
|
||||
class DatabaseTest extends UnitTestCase {
|
||||
|
||||
function testUserFinderReadsResultsFromDatabase() {<strong>
|
||||
$result = new MockResultIterator();
|
||||
$result->returns('next', false);
|
||||
$result->returnsAt(0, 'next', array(1, 'tom'));
|
||||
$result->returnsAt(1, 'next', array(3, 'dick'));
|
||||
$result->returnsAt(2, 'next', array(6, 'harry'));
|
||||
|
||||
$connection = new MockDatabaseConnection();
|
||||
$connection->returns('selectQuery', $result);</strong>
|
||||
|
||||
$finder = new UserFinder(<strong>$connection</strong>);
|
||||
$this->assertIdentical(
|
||||
$finder->findNames(),
|
||||
array('tom', 'dick', 'harry'));
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
Now only if our
|
||||
<span class="new_code">$connection</span> is called with the
|
||||
<span class="new_code">query()</span> method 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>
|
||||
We could refine this test further by insisting that the correct
|
||||
query is sent...
|
||||
<pre>
|
||||
$connection->returns('selectQuery', $result, array(<strong>'select name, id from people'</strong>));
|
||||
</pre>
|
||||
This is actually a bad idea.
|
||||
</p>
|
||||
<p>
|
||||
We have a <span class="new_code">UserFinder</span> in object land, talking to
|
||||
database tables using a large interface - the whole of SQL.
|
||||
Imagine that we have written a lot of tests that now depend
|
||||
on the exact SQL string passed.
|
||||
These queries could change en masse for all sorts of reasons
|
||||
not related to the specific test.
|
||||
For example the quoting rules could change, a table name could
|
||||
change, a link table added or whatever.
|
||||
This would require the rewriting of every single test any time
|
||||
one of these refactoring is made, yet the intended behaviour has
|
||||
stayed the same.
|
||||
Tests are supposed to help refactoring, not hinder it.
|
||||
I'd certainly like to have a test suite that passes while I change
|
||||
table names.
|
||||
</p>
|
||||
<p>
|
||||
As a rule it is best not to mock a fat interface.
|
||||
</p>
|
||||
<p>
|
||||
By contrast, here is the round trip test...
|
||||
<pre>
|
||||
class DatabaseTest extends UnitTestCase {<strong>
|
||||
function setUp() { ... }
|
||||
function tearDown() { ... }</strong>
|
||||
|
||||
function testUserFinderReadsResultsFromDatabase() {
|
||||
$finder = new UserFinder(<strong>new DatabaseConnection()</strong>);
|
||||
$finder->add('tom');
|
||||
$finder->add('dick');
|
||||
$finder->add('harry');
|
||||
$this->assertIdentical(
|
||||
$finder->findNames(),
|
||||
array('tom', 'dick', 'harry'));
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
This test is immune to schema changes.
|
||||
It will only fail if you actually break the functionality, which
|
||||
is what you want a test to do.
|
||||
</p>
|
||||
<p>
|
||||
The catch is those <span class="new_code">setUp()</span> and <span class="new_code">tearDown()</span>
|
||||
methods that we've rather glossed over.
|
||||
They have to clear out the database tables and ensure that the
|
||||
schema is defined correctly.
|
||||
That can be a chunk of extra work, but you usually have this code
|
||||
lying around anyway for deployment purposes.
|
||||
</p>
|
||||
<p>
|
||||
One place where you definitely need a mock is simulating failures.
|
||||
Say the database goes down while <span class="new_code">UserFinder</span> is doing
|
||||
it's work.
|
||||
Does <span class="new_code">UserFinder</span> behave well...?
|
||||
<pre>
|
||||
class DatabaseTest extends UnitTestCase {
|
||||
|
||||
function testUserFinder() {
|
||||
$connection = new MockDatabaseConnection();<strong>
|
||||
$connection->throwOn('selectQuery', new TimedOut('Ouch!'));</strong>
|
||||
$alert = new MockAlerts();<strong>
|
||||
$alert->expectOnce('notify', 'Database is busy - please retry');</strong>
|
||||
$finder = new UserFinder($connection, $alert);
|
||||
$this->assertIdentical($finder->findNames(), array());
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
We've passed the <span class="new_code">UserFinder</span> an <span class="new_code">$alert</span>
|
||||
object.
|
||||
This is going to do some kind of pretty notifications in the
|
||||
user interface in the event of an error.
|
||||
We'd rather not sprinkle our code with hard wired <span class="new_code">print</span>
|
||||
statements if we can avoid it.
|
||||
Wrapping the means of output means we can use this code anywhere.
|
||||
It also makes testing easier.
|
||||
</p>
|
||||
<p>
|
||||
To pass this test, the finder has to write a nice user friendly
|
||||
message to <span class="new_code">$alert</span>, rather than propogating the exception.
|
||||
So far, so good.
|
||||
</p>
|
||||
<p>
|
||||
How do we get the mock <span class="new_code">DatabaseConnection</span> to throw an exception?
|
||||
We generate the exception using the <span class="new_code">throwOn</span> method
|
||||
on the mock.
|
||||
</p>
|
||||
<p>
|
||||
Finally, what if the method you want to simulate does not exist yet?
|
||||
If you set a return value on a method that is not there, SimpleTest
|
||||
will throw an error.
|
||||
What if you are using <span class="new_code">__call()</span> to simulate dynamic methods?
|
||||
</p>
|
||||
<p>
|
||||
Objects with dynamic interfaces, using <span class="new_code">__call</span>, can
|
||||
be problematic with the current mock objects implementation.
|
||||
You can mock the <span class="new_code">__call()</span> method, but this is ugly.
|
||||
Why should a test know anything about such low level implementation details?
|
||||
It just wants to simulate the call.
|
||||
</p>
|
||||
<p>
|
||||
The way round this is to add extra methods to the mock when
|
||||
generating it.
|
||||
<pre>
|
||||
<strong>Mock::generate('DatabaseConnection', 'MockDatabaseConnection', array('setOptions'));</strong>
|
||||
</pre>
|
||||
In a large test suite this could cause trouble, as you probably
|
||||
already have a mock version of the class called
|
||||
<span class="new_code">MockDatabaseConnection</span> without the extra methods.
|
||||
The code generator will not generate a mock version of the class if
|
||||
one of the same name already exists.
|
||||
You will confusingly fail to see your method if another definition
|
||||
was run first.
|
||||
</p>
|
||||
<p>
|
||||
To cope with this, SimpleTest allows you to choose any name for the
|
||||
new class at the same time as you add the extra methods.
|
||||
<pre>
|
||||
Mock::generate('DatabaseConnection', <strong>'MockDatabaseConnectionWithOptions'</strong>, array('setOptions'));
|
||||
</pre>
|
||||
Here the mock will behave as if the <span class="new_code">setOptions()</span>
|
||||
existed in the original class.
|
||||
</p>
|
||||
<p>
|
||||
Mock objects can only be used within test cases, as upon expectations
|
||||
they send messages straight to the currently running test case.
|
||||
Creating them outside a test case will cause a run time error
|
||||
when an expectation is triggered and there is no running test case
|
||||
for the message to end up.
|
||||
We cover expectations next.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="expectations"></a>Mocks as critics</h2>
|
||||
<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, let's take a simple <span class="new_code">PageController</span>
|
||||
class to handle a credit card payment form...
|
||||
<pre>
|
||||
class PaymentForm extends PageController {
|
||||
function __construct($alert, $payment_gateway) { ... }
|
||||
function makePayment($request) { ... }
|
||||
}
|
||||
</pre>
|
||||
This class takes a <span class="new_code">PaymentGateway</span> to actually talk
|
||||
to the bank.
|
||||
It also takes an <span class="new_code">Alert</span> object to handle errors.
|
||||
This class talks to the page or template.
|
||||
It's responsible for painting the error message and highlighting any
|
||||
form fields that are incorrect.
|
||||
</p>
|
||||
<p>
|
||||
It might look something like...
|
||||
<pre>
|
||||
class Alert {
|
||||
function warn($warning, $id) {
|
||||
print '<div class="warning">' . $warning . '</div>';
|
||||
print '<style type="text/css">#' . $id . ' { background-color: red }</style>';
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
Our interest is in the <span class="new_code">makePayment()</span> method.
|
||||
If we fail to enter a "CVV2" number (the three digit number
|
||||
on the back of the credit card), we want to show an error rather than
|
||||
try to process the payment.
|
||||
In test form...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
require_once('payment_form.php');
|
||||
Mock::generate('Alert');
|
||||
Mock::generate('PaymentGateway');
|
||||
|
||||
class PaymentFormFailuresShouldBeGraceful extends UnitTestCase {
|
||||
|
||||
function testMissingCvv2CausesAlert() {
|
||||
$alert = new MockAlert();
|
||||
<strong>$alert->expectOnce(
|
||||
'warn',
|
||||
array('Missing three digit security code', 'cvv2'));</strong>
|
||||
$controller = new PaymentForm(<strong>$alert</strong>, new MockPaymentGateway());
|
||||
$controller->makePayment($this->requestWithMissingCvv2());
|
||||
}
|
||||
|
||||
function requestWithMissingCvv2() { ... }
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
The first question you may be asking is, where are the assertions?
|
||||
</p>
|
||||
<p>
|
||||
The call to <span class="new_code">expectOnce('warn', array(...))</span> instructs the mock
|
||||
to expect a call to <span class="new_code">warn()</span> before the test ends.
|
||||
When it gets a call to <span class="new_code">warn()</span>, it checks the arguments.
|
||||
If the arguments don't match, then a failure is generated.
|
||||
It also fails if the method is never called at all.
|
||||
</p>
|
||||
<p>
|
||||
The test above not only asserts that <span class="new_code">warn</span> was called,
|
||||
but that it received the string "Missing three digit security code"
|
||||
and also the tag "cvv2".
|
||||
The equivalent of <span class="new_code">assertIdentical()</span> is applied to both
|
||||
fields when the parameters are compared.
|
||||
</p>
|
||||
<p>
|
||||
If you are not interested in the actual message, and we are not
|
||||
for user interface code that changes often, we can skip that
|
||||
parameter with the "*" operator...
|
||||
<pre>
|
||||
class PaymentFormFailuresShouldBeGraceful extends UnitTestCase {
|
||||
|
||||
function testMissingCvv2CausesAlert() {
|
||||
$alert = new MockAlert();
|
||||
$alert->expectOnce('warn', array(<strong>'*'</strong>, 'cvv2'));
|
||||
$controller = new PaymentForm($alert, new MockPaymentGateway());
|
||||
$controller->makePayment($this->requestWithMissingCvv2());
|
||||
}
|
||||
|
||||
function requestWithMissingCvv2() { ... }
|
||||
}
|
||||
</pre>
|
||||
We can weaken the test further by missing
|
||||
out the parameters array...
|
||||
<pre>
|
||||
function testMissingCvv2CausesAlert() {
|
||||
$alert = new MockAlert();
|
||||
<strong>$alert->expectOnce('warn');</strong>
|
||||
$controller = new PaymentForm($alert, new MockPaymentGateway());
|
||||
$controller->makePayment($this->requestWithMissingCvv2());
|
||||
}
|
||||
</pre>
|
||||
This will only test that the method is called,
|
||||
which is a bit drastic in this case.
|
||||
Later on, we'll see how we can weaken the expectations more precisely.
|
||||
</p>
|
||||
<p>
|
||||
Tests without assertions can be both compact and very expressive.
|
||||
Because we intercept the call on the way into an object, here of
|
||||
the <span class="new_code">Alert</span> class, we avoid having to assert its state
|
||||
afterwards.
|
||||
This not only avoids the assertions in the tests, but also having
|
||||
to add extra test only accessors to the original code.
|
||||
If you catch yourself adding such accessors, called "state based testing",
|
||||
it's probably time to consider using mocks in the tests.
|
||||
This is called "behaviour based testing", and is normally superior.
|
||||
</p>
|
||||
<p>
|
||||
Let's add another test.
|
||||
Let's make sure that we don't even attempt a payment without a CVV2...
|
||||
<pre>
|
||||
class PaymentFormFailuresShouldBeGraceful extends UnitTestCase {
|
||||
|
||||
function testMissingCvv2CausesAlert() { ... }
|
||||
|
||||
function testNoPaymentAttemptedWithMissingCvv2() {
|
||||
$payment_gateway = new MockPaymentGateway();
|
||||
<strong>$payment_gateway->expectNever('pay');</strong>
|
||||
$controller = new PaymentForm(new MockAlert(), $payment_gateway);
|
||||
$controller->makePayment($this->requestWithMissingCvv2());
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
Asserting a negative can be very hard in tests, but
|
||||
<span class="new_code">expectNever()</span> makes it easy.
|
||||
</p>
|
||||
<p>
|
||||
<span class="new_code">expectOnce()</span> and <span class="new_code">expectNever()</span> are
|
||||
sufficient for most tests, but
|
||||
occasionally you want to test multiple events.
|
||||
Normally for usability purposes we want all missing fields
|
||||
in the form to light up, not just the first one.
|
||||
This means that we should get multiple calls to
|
||||
<span class="new_code">Alert::warn()</span>, not just one...
|
||||
<pre>
|
||||
function testAllRequiredFieldsHighlightedOnEmptyRequest() {
|
||||
$alert = new MockAlert();<strong>
|
||||
$alert->expectAt(0, 'warn', array('*', 'cc_number'));
|
||||
$alert->expectAt(1, 'warn', array('*', 'expiry'));
|
||||
$alert->expectAt(2, 'warn', array('*', 'cvv2'));
|
||||
$alert->expectAt(3, 'warn', array('*', 'card_holder'));
|
||||
$alert->expectAt(4, 'warn', array('*', 'address'));
|
||||
$alert->expectAt(5, 'warn', array('*', 'postcode'));
|
||||
$alert->expectAt(6, 'warn', array('*', 'country'));
|
||||
$alert->expectCallCount('warn', 7);</strong>
|
||||
$controller = new PaymentForm($alert, new MockPaymentGateway());
|
||||
$controller->makePayment($this->requestWithMissingCvv2());
|
||||
}
|
||||
</pre>
|
||||
The counter in <span class="new_code">expectAt()</span> is the number of times
|
||||
the method has been called already.
|
||||
Here we are asserting that every field will be highlighted.
|
||||
</p>
|
||||
<p>
|
||||
Note that we are forced to assert the order too.
|
||||
SimpleTest does not yet have a way to avoid this, but
|
||||
this will be fixed in future versions.
|
||||
</p>
|
||||
<p>
|
||||
Here is the full list of expectations you can set on a mock object
|
||||
in <a href="http://simpletest.org/">SimpleTest</a>.
|
||||
As with the assertions, these methods take an optional failure message.
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>Expectation</th>
|
||||
<th>Description</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="new_code">expect($method, $args)</span></td>
|
||||
<td>Arguments must match if called</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectAt($timing, $method, $args)</span></td>
|
||||
<td>Arguments must match when called on the <span class="new_code">$timing</span>'th time</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectCallCount($method, $count)</span></td>
|
||||
<td>The method must be called exactly this many times</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectMaximumCallCount($method, $count)</span></td>
|
||||
<td>Call this method no more than <span class="new_code">$count</span> times</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectMinimumCallCount($method, $count)</span></td>
|
||||
<td>Must be called at least <span class="new_code">$count</span> times</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectNever($method)</span></td>
|
||||
<td>Must never be called</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectOnce($method, $args)</span></td>
|
||||
<td>Must be called once and with the expected arguments if supplied</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectAtLeastOnce($method, $args)</span></td>
|
||||
<td>Must be called at least once, and always with any expected arguments</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 and the count is for
|
||||
each method independently.
|
||||
</dd>
|
||||
<dt class="new_code">$count</dt>
|
||||
<dd>The number of calls expected.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<p>
|
||||
If you have just 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 allow the method to never be called.
|
||||
Checking the arguments and the overall call count
|
||||
are currently independant.
|
||||
Add an <span class="new_code">expectCallCount()</span> expectation when you
|
||||
are using <span class="new_code">expectAt()</span> unless zero calls is allowed.
|
||||
</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>
|
||||
|
||||
</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>
|
||||
487
videodb/test/simpletest/docs/en/overview.html
Normal file
487
videodb/test/simpletest/docs/en/overview.html
Normal file
@@ -0,0 +1,487 @@
|
||||
<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>
|
||||
</ul>
|
||||
<div class="content">
|
||||
<h2>
|
||||
<a class="target" name="summary"></a>What is SimpleTest?</h2>
|
||||
<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.
|
||||
Each test method of a test case class 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
|
||||
or unexpected exception triggers an alert and a description
|
||||
of the mismatch.
|
||||
These test case declarations are transformed into executable
|
||||
test scripts by including a SimpleTest aurorun.php file.
|
||||
</p>
|
||||
<p>
|
||||
These documents apply to SimpleTest version 1.1, although we
|
||||
try hard to maintain compatibility between versions.
|
||||
If you get a test failure after an upgrade, check out the
|
||||
file "HELP_MY_TESTS_DONT_WORK_ANYMORE" in the
|
||||
simpletest directory to see if a feature you are using
|
||||
has since been deprecated and later removed.
|
||||
</p>
|
||||
<p>
|
||||
A <a href="unit_test_documentation.html">test case</a> looks like this...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
class <strong>CanAddUp</strong> extends UnitTestCase {<strong>
|
||||
function testOneAndOneMakesTwo() {
|
||||
$this->assertEqual(1 + 1, 2);
|
||||
}</strong>
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
Tests are grouped into test cases, which are just
|
||||
PHP classes that extend <span class="new_code">UnitTestCase</span>
|
||||
or <span class="new_code">WebTestCase</span>.
|
||||
The tests themselves are just normal methods that start
|
||||
their name with the letters "test".
|
||||
You can have as many test cases as you want in a test
|
||||
script and each test case can have as many test methods
|
||||
as it wants too.
|
||||
</p>
|
||||
<p>
|
||||
This test script is immediately runnable.
|
||||
You just invoke it on the command line like so...
|
||||
<pre class="shell">
|
||||
php adding_test.php
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
When run on the command line you should see something like...
|
||||
<pre class="shell">
|
||||
adding_test.php
|
||||
OK
|
||||
Test cases run: 1/1, Passes: 1, Failures: 0, Exceptions: 0
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
If you place it on a web server and point your
|
||||
web browser at it...
|
||||
<div class="demo">
|
||||
<h1>adding_test.php</h1>
|
||||
<div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
|
||||
<strong>6</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
Of course this is a silly example.
|
||||
A more realistic example might be...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
require_once('log.php');
|
||||
|
||||
class <strong>TestOfLogging</strong> extends UnitTestCase {
|
||||
function testWillCreateLogFileOnFirstMessage() {
|
||||
$log = new Log('my.log');
|
||||
$this->assertFalse(file_exists('my.log'));
|
||||
$log->message('Hello');
|
||||
$this->assertTrue(file_exists('my.log'));
|
||||
}</strong>
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
This tool is designed for the developer.
|
||||
The target audience of this tool is anyone developing a small
|
||||
to medium PHP application, including developers new to
|
||||
unit and web regression testing.
|
||||
The core principles are ease of use first, with extendibility and
|
||||
essential features.
|
||||
</p>
|
||||
<p>
|
||||
Tests are written in the PHP language itself more or less
|
||||
as the application itself is built.
|
||||
The advantage of using PHP 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 when 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 testHomePageHasContactDetailsLink() {
|
||||
$this->get('http://www.my-site.com/index.php');
|
||||
$this->assertTitle('My Home Page');
|
||||
$this->clickLink('Contact');
|
||||
$this->assertTitle('Contact me');
|
||||
$this->assertText('/Email me at/');
|
||||
}</strong>
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="features"></a>Feature list</h2>
|
||||
<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.
|
||||
</p>
|
||||
<p>
|
||||
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: green;">1.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Alternate HTML parsers</td>
|
||||
<td>Can detect compiled parsers for performance improvements</td>
|
||||
<td style="color: green;">1.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>REST support</td>
|
||||
<td>Support for REST verbs as put(), delete(), etc.</td>
|
||||
<td style="color: green;">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>Plug-in architecture</td>
|
||||
<td>Automatic import of extensions including command line options</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 as well as UTF support</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 other 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>Integrated Selenium support</td>
|
||||
<td>Easy to use built in Selenium driver and tutorial or similar browser automation</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>
|
||||
PHP 5 migration is complete, which means that only PHP 5.0.3+
|
||||
will be supported in SimpleTest version 1.1+.
|
||||
Earlier versions of SimpleTest are compatible with PHP 4.2 up to
|
||||
PHP 5 (non E_STRICT).
|
||||
</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>
|
||||
457
videodb/test/simpletest/docs/en/partial_mocks_documentation.html
Normal file
457
videodb/test/simpletest/docs/en/partial_mocks_documentation.html
Normal file
@@ -0,0 +1,457 @@
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="inject"></a>The mock injection problem</h2>
|
||||
<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();
|
||||
...
|
||||
$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();
|
||||
...
|
||||
$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();
|
||||
...
|
||||
$network = new MockNetwork();
|
||||
$network->returnsByReference('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>
|
||||
Well techniques like "Dependency Injection" tackle the problem of
|
||||
instantiating a lot of constructor parameters.
|
||||
Unfortunately knowledge of this pattern is not widespread, and if you
|
||||
are trying to get older code to work, rearchitecting the whole
|
||||
application is not really an option.
|
||||
</p>
|
||||
<p>
|
||||
Is there a middle ground?
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="creation"></a>Protected factory method</h2>
|
||||
<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>
|
||||
|
||||
protected function createSocket($ip, $port) {
|
||||
return new Socket($ip, $port);
|
||||
}</strong>
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
This is a pretty safe step even for very tangled legacy code.
|
||||
This is the only change we make to the application.
|
||||
</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();
|
||||
}
|
||||
|
||||
protected 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();
|
||||
...
|
||||
$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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="partial"></a>A partial mock</h2>
|
||||
<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();
|
||||
...
|
||||
$telnet = new TelnetTestVersion();
|
||||
$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();
|
||||
...
|
||||
$telnet = new TelnetTestVersion();
|
||||
$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');
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
Partial mocks are not used often.
|
||||
I consider them transitory.
|
||||
Useful while refactoring, but once the application has
|
||||
all of it's dependencies nicely separated then the
|
||||
partial mocks can wither away.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="less"></a>Testing less than a class</h2>
|
||||
<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've hardly
|
||||
tried it.
|
||||
I am 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 if you allow test mechanisms like this.
|
||||
</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>
|
||||
616
videodb/test/simpletest/docs/en/reporter_documentation.html
Normal file
616
videodb/test/simpletest/docs/en/reporter_documentation.html
Normal file
@@ -0,0 +1,616 @@
|
||||
<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>
|
||||
<a href="#xml">Using XML</a> for remote testing
|
||||
</li>
|
||||
</ul>
|
||||
<div class="content">
|
||||
|
||||
<p>
|
||||
SimpleTest pretty much follows the MVC-ish 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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="html"></a>Reporting results in HTML</h2>
|
||||
<p>
|
||||
The default HTML 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 __construct($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 title, 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, top level test case, 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 ReporterShowingPasses 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";
|
||||
}
|
||||
|
||||
protected 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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="other"></a>Extending the reporter</h2>
|
||||
<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/simpletest.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>
|
||||
}
|
||||
|
||||
function paintError($message) {<strong>
|
||||
parent::paintError($message);</strong>
|
||||
}
|
||||
|
||||
function paintException($exception) {<strong>
|
||||
parent::paintException($exception);</strong>
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
No output would come from this class until you add it.
|
||||
</p>
|
||||
<p>
|
||||
The catch with using this low level class is that you must
|
||||
explicitely invoke it in the test script.
|
||||
The "autorun" facility will not be able to use
|
||||
its runtime context (whether it's running in a web browser
|
||||
or the command line) to select the reporter.
|
||||
</p>
|
||||
<p>
|
||||
You explicitely invoke the test runner like so...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
$test = new TestSuite('File test');
|
||||
$test->addFile('tests/file_test.php');
|
||||
$test->run(<strong>new MyReporter()</strong>);
|
||||
?>
|
||||
</pre>
|
||||
...perhaps like this...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/simpletest.php');
|
||||
require_once('my_reporter.php');
|
||||
|
||||
class MyTest extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addFile('tests/file_test.php');
|
||||
}
|
||||
}
|
||||
|
||||
$test = new MyTest();
|
||||
$test->run(<strong>new MyReporter()</strong>);
|
||||
?>
|
||||
</pre>
|
||||
We'll show how to fit in with "autorun" later.
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="cli"></a>The command line reporter</h2>
|
||||
<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 explicitely, substitute it
|
||||
for the HTML version...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
$test = new TestSuite('File test');
|
||||
$test->addFile('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, Passes: 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, Passes: 0, 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/autorun.php');
|
||||
|
||||
$test = new TestSuite('File test');
|
||||
$test->addFile('tests/file_test.php');
|
||||
<strong>exit ($test->run(new TextReporter()) ? 0 : 1);</strong>
|
||||
?>
|
||||
</pre>
|
||||
Of course we wouldn'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/autorun.php');
|
||||
|
||||
$test = new TestSuite('File test');
|
||||
$test->addFile('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.
|
||||
When you use the "autorun.php", and no
|
||||
test has been run by the end, this is pretty much
|
||||
the code that SimpleTest will run for you implicitely.
|
||||
</p>
|
||||
<p>
|
||||
In other words, this is gives the same result...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/autorun.php');
|
||||
|
||||
class MyTest extends TestSuite {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addFile('tests/file_test.php');
|
||||
}
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="xml"></a>Remote testing</h2>
|
||||
<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>
|
||||
To get your normal test cases to produce this format, on the
|
||||
command line add the <span class="new_code">--xml</span> flag.
|
||||
<pre class="shell">
|
||||
php my_test.php --xml
|
||||
</pre>
|
||||
You can do teh same thing in the web browser by adding the
|
||||
URL parameter <span class="new_code">xml=1</span>.
|
||||
Any true value will do.
|
||||
</p>
|
||||
<p>
|
||||
You can consume 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>
|
||||
Most likely it's when you want to isolate a problematic crash
|
||||
prone test.
|
||||
You can collect the XML output using the backtick operator
|
||||
from another test.
|
||||
In that way it runs in its own process...
|
||||
<pre>
|
||||
<?php
|
||||
require_once('simpletest/xml.php');
|
||||
|
||||
if (TextReporter::inCli()) {
|
||||
$parser = new SimpleTestXmlParser(new TextReporter());
|
||||
} else {
|
||||
$parser = new SimpleTestXmlParser(new HtmlReporter());
|
||||
}
|
||||
$parser->parse(`php flakey_test.php --xml`);
|
||||
?>
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
Another use is breaking up large test suites.
|
||||
</p>
|
||||
<p>
|
||||
A problem with large test suites is thet they can exhaust
|
||||
the default 16Mb 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('simpletest/autorun.php');
|
||||
|
||||
$test_url = ...;
|
||||
$dry_url = ...;
|
||||
|
||||
class MyTestOnAnotherServer extends RemoteTestCase {
|
||||
function __construct() {
|
||||
$test_url = ...
|
||||
parent::__construct($test_url, $test_url . ' --dry');
|
||||
}
|
||||
}
|
||||
?>
|
||||
</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 test suite.
|
||||
</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>
|
||||
442
videodb/test/simpletest/docs/en/unit_test_documentation.html
Normal file
442
videodb/test/simpletest/docs/en/unit_test_documentation.html
Normal file
@@ -0,0 +1,442 @@
|
||||
<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">
|
||||
<h2>
|
||||
<a class="target" name="unit"></a>Unit test cases</h2>
|
||||
<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 interference 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>Fail if matching error does not occour</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">expectException($x)</span></td>
|
||||
<td>Fail if matching exception is not thrown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="new_code">ignoreException($x)</span></td>
|
||||
<td>Swallows any upcoming matching exception</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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="extending_unit"></a>Extending test cases</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="running_unit"></a>Running a single test case</h2>
|
||||
<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>
|
||||
588
videodb/test/simpletest/docs/en/web_tester_documentation.html
Normal file
588
videodb/test/simpletest/docs/en/web_tester_documentation.html
Normal file
@@ -0,0 +1,588 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>SimpleTest 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">
|
||||
<h2>
|
||||
<a class="target" name="fetch"></a>Fetching a page</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="content"></a>Testing page content</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="navigation"></a>Navigating a web site</h2>
|
||||
<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>
|
||||
|
||||
<h2>
|
||||
<a class="target" name="request"></a>Modifying the request</h2>
|
||||
<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>
|
||||
Reference in New Issue
Block a user