- 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.
-
- 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...
-
1/1 test cases complete.
- 0 passes, 0 fails and 0 exceptions.
-
- 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...
-
- Any one of these tests would normally do on it's own depending
- on the amount of detail you want to see.
-
-
- 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...
-
- 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.
-
-
- You can shortcut this step further by encoding the log in
- details straight into the URL...
-
- If your username or password has special characters, then you
- will have to URL encode them or the request will not be parsed
- correctly.
- Also this header will not be sent on subsequent requests if
- you request a page with a fully qualified URL.
- If you navigate with relative URLs though, the authentication
- information will be preserved.
-
-
- Only basic authentication is currently supported and this is
- only really secure in tandem with HTTPS connections.
- This is usually enough to protect test server from prying eyes,
- however.
- Digest authentication and NTLM authentication may be added
- in the future.
-
- Basic authentication doesn't give enough control over the
- user interface for web developers.
- More likely this functionality will be coded directly into
- the web architecture using cookies and complicated timeouts.
-
- 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...
-
- 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.
-
-
- The rest of the test would be the same as any other form,
- but we might want to confirm that we still have the same
- cookie after log-in as before we entered.
- We wouldn't want to lose track of this after all.
- Here is a possible test for this...
-
- 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...
-
- The WebTestCase::restart() 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.
-
-
- Expiring cookies can be a problem.
- After all, if you have a cookie that expires after an hour,
- you don't want to stall the test for an hour while the
- cookie passes it's timeout.
-
-
- To push the cookies over the hour limit you can age them
- before you restart the session...
-
- SimpleTest's web browser component can be used not just
- outside of the WebTestCase class, but also
- independently of the SimpleTest framework itself.
-
- 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 PHP web site...
-
- There are simpler methods to do this particular example in PHP
- of course.
- For example you can just use the PHP file()
- command against what here is a pretty fixed page.
- However, using the web browser for scripts allows authentication,
- correct handling of cookies, automatic loading of frames, redirects,
- form submission and the ability to examine the page headers.
- Such methods are fragile against a site that is constantly
- evolving and you would want a more direct way of accessing
- data in a permanent set up, but for simple tasks this can provide
- a very rapid solution.
-
-
- All of the navigation methods used in the
- WebTestCase
- are present in the SimpleBrowser class, but
- the assertions are replaced with simpler accessors.
- Here is a full list of the page navigation methods...
-
-
-
-
addHeader($header)
Adds a header to every fetch
-
-
-
useProxy($proxy, $username, $password)
Use this proxy from now on
-
-
-
head($url, $parameters)
Perform a HEAD request
-
-
-
get($url, $parameters)
Fetch a page with GET
-
-
-
post($url, $parameters)
Fetch a page with POST
-
-
-
clickLink($label)
Follows a link by label
-
-
-
isLink($label)
See if a link is present by label
-
-
-
clickLinkById($id)
Follows a link by attribute
-
-
-
isLinkById($id)
See if a link is present by attribut
-
-
-
getUrl()
Current URL of page or frame
-
-
-
getTitle()
Page title
-
-
-
getContent()
Raw page or frame
-
-
-
getContentAsText()
HTML removed except for alt text
-
-
-
retry()
Repeat the last request
-
-
-
back()
Use the browser back button
-
-
-
forward()
Use the browser forward button
-
-
-
authenticate($username, $password)
Retry page or frame after a 401 response
-
-
-
restart($date)
Restarts the browser for a new session
-
-
-
ageCookies($interval)
Ages the cookies by the specified time
-
-
-
setCookie($name, $value)
Sets an additional cookie
-
-
-
getCookieValue($host, $path, $name)
Reads the most specific cookie
-
-
-
getCurrentCookieValue($name)
Reads cookie for the current context
-
-
-
- The methods SimpleBrowser::useProxy() and
- SimpleBrowser::addHeader() are special.
- Once called they continue to apply to all subsequent fetches.
-
-
- At the moment there aren't any methods to list available forms
- and fields.
- This will probably be added to later versions of SimpleTest.
-
-
- 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.
-
-
-
-
getFrames()
A dump of the current frame structure
-
-
-
getFrameFocus()
Current frame label or index
-
-
-
setFrameFocusByIndex($choice)
Select a frame numbered from 1
-
-
-
setFrameFocus($name)
Select frame by label
-
-
-
clearFrameFocus()
Treat all the frames as a single page
-
-
-
- When focused on a single frame, the content will come from
- that frame only.
- This includes links to click and forms to submit.
-
-
-
- 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...
-
-
-
-
setConnectionTimeout($timeout)
Close the socket on overrun
-
-
-
getRequest()
Raw request header of page or frame
-
-
-
getHeaders()
Raw response header of page or frame
-
-
-
getTransportError()
Any socket level errors in the last fetch
-
-
-
getResponseCode()
HTTP response of page or frame
-
-
-
getMimeType()
Mime type of page or frame
-
-
-
getAuthentication()
Authentication type in 401 challenge header
-
-
-
getRealm()
Authentication realm in 401 challenge header
-
-
-
setMaximumRedirects($max)
Number of redirects before page is loaded anyway
-
-
-
setMaximumNestedFrames($max)
Protection against recursive framesets
-
-
-
ignoreFrames()
Disables frames support
-
-
-
useFrames()
Enables frames support
-
-
-
- The methods SimpleBrowser::setConnectionTimeout()
- SimpleBrowser::setMaximumRedirects(),
- SimpleBrowser::setMaximumNestedFrames(),
- SimpleBrowser::ignoreFrames() and
- SimpleBrowser::useFrames() 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 SimpleBrowser::setFrameFocus()
- and you can then use SimpleBrowser::getRequest(), etc to
- see what happened.
-
-
-
- Anything that could be done in a
- WebTestCase can
- now be done in a UnitTestCase.
- This means that we can freely mix domain object testing with the
- web interface...
-
- 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.
-
-
- A more useful case of where using the browser directly can be helpful
- is where the WebTestCase cannot cope.
- An example is where two browsers are needed at the same time.
-
-
- For example, say we want to disallow multiple simultaneous
- usage of a site with the same username.
- This test case will do the job...
-
- The default behaviour of the
- mock objects
- in
- SimpleTest
- 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.
-
-
- 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.
-
-
- For example, suppose we have a news service that has failed
- to connect to its remote source.
-
-class NewsService {
- ...
- function publish(&$writer) {
- if (! $this->isConnected()) {
- $writer->write('Cannot connect to news service "' .
- $this->_name . '" at this time. ' .
- 'Please try again later.');
- }
- ...
- }
-}
-
- Here it is sending its content to a
- Writer class.
- We could test this behaviour with a
- MockWriter like so...
-
-class TestOfNewsService extends UnitTestCase {
- ...
- function testConnectionFailure() {
- $writer = &new MockWriter($this);
- $writer->expectOnce('write', array(
- 'Cannot connect to news service ' .
- '"BBC News" at this time. ' .
- 'Please try again later.'));
-
- $service = &new NewsService('BBC News');
- $service->publish($writer);
-
- $writer->tally();
- }
-}
-
- 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.
-
-
- To get around this, we would like to do a regular expression
- test rather than an exact match.
- We can actually do this with...
-
- Instead of passing in the expected parameter to the
- MockWriter we pass an
- expectation class called
- WantedPatternExpectation.
- 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.
-
-
- The WantedPatternExpectation takes
- the regular expression to match in its constructor.
- Whenever a comparison is made by the MockWriter
- against this expectation class, it will do a
- preg_match() 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.
-
-
- The possible expectation classes are...
-
-
-
-
EqualExpectation
An equality, rather than the stronger identity comparison
-
-
-
NotEqualExpectation
An inequality comparison
-
-
-
IndenticalExpectation
The default mock object check which must match exactly
-
-
-
NotIndenticalExpectation
Inverts the mock object logic
-
-
-
WantedPatternExpectation
Uses a Perl Regex to match a string
-
-
-
NoUnwantedPatternExpectation
Passes only if failing a Perl Regex
-
-
-
IsAExpectation
Checks the type or class name only
-
-
-
NotAExpectation
Opposite of the IsAExpectation
-
-
-
MethodExistsExpectation
Checks a method is available on an object
-
-
-
- Most take the expected value in the constructor.
- The exceptions are the pattern matchers, which take a regular expression,
- and the IsAExpectation and NotAExpectation which takes a type
- or class name as a string.
-
-
-
- The expectation classes can be used not just for sending assertions
- from mock objects, but also for selecting behaviour for either
- the
- mock objects
- or the
- server stubs.
- Anywhere a list of arguments is given, a list of expectation objects
- can be inserted instead.
-
-
- Suppose we want an authorisation server stub to simulate a successful login
- only if it receives a valid session object.
- We can do this as follows...
-
-Stub::generate('Authorisation');
-
-$authorisation = new StubAuthorisation();
-$authorisation->setReturnValue(
- 'isAllowed',
- true,
- array(new IsAExpectation('Session', 'Must be a session')));
-$authorisation->setReturnValue('isAllowed', false);
-
- We have set the default stub behaviour to return false when
- isAllowed is called.
- When we call the method with a single parameter that
- is a Session 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.
-
-
- This kind of sophistication is rarely useful, but is included for
- completeness.
-
- The expectation classes have a very simple structure.
- So simple that it is easy to create your own versions for
- commonly used test logic.
-
-
- 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
- SimpleExpectation...
-
-class ValidIp extends SimpleExpectation {
-
- function test($ip) {
- return (ip2long($ip) != -1);
- }
-
- function testMessage($ip) {
- return "Address [$ip] should be a valid IP address";
- }
-}
-
- There are only two methods to implement.
- The test() method should
- evaluate to true if the expectation is to pass, and
- false otherwise.
- The testMessage() method
- should simply return some helpful text explaining the test
- that was carried out.
-
-
- This class can now be used in place of the earlier expectation
- classes.
-
- The SimpleTest unit testing framework
- also uses the expectation classes internally for the
- UnitTestCase class.
- We can also take advantage of these mechanisms to reuse our
- homebrew expectation classes within the test suites directly.
-
-
- The most crude way of doing this is to use the
- SimpleTest::assertExpectation() method to
- test against it directly...
-
- This is a little untidy compared with our usual
- assert...() syntax.
-
-
- 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...
-
- It is unlikely we would ever need this degree of control
- over the testing machinery.
- It is rare to need the expectations for more than pattern
- matching.
- Also, complex expectation classes could make the tests
- harder to read and debug.
- These mechanisms are really of most use to authors of systems
- that will extend the test framework to create their own tool set.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
-
-
diff --git a/tests/UnitTests/simpletest/docs/en/form_testing_documentation.html b/tests/UnitTests/simpletest/docs/en/form_testing_documentation.html
deleted file mode 100644
index 597db3b0..00000000
--- a/tests/UnitTests/simpletest/docs/en/form_testing_documentation.html
+++ /dev/null
@@ -1,277 +0,0 @@
-
-
-
-Simple Test documentation for testing HTML forms
-
-
-
-
- When a page is fetched by the WebTestCase
- using get() or
- post() 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...
-
- We can navigate to this code, via the
- LastCraft
- site, with the following test...
-
-class SimpleFormTests extends WebTestCase {
-
- function testDefaultValue() {
- $this->get('http://www.lastcraft.com/form_testing_documentation.php');
- $this->assertField('a', 'A default');
- }
-}
-
- 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"
-
-
- We could submit the form straight away, but first we'll change
- the value of the text field and only then submit it...
-
- 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 GET
- request back to the same location.
- SimpleTest tries to emulate typical browser behaviour as much as possible,
- rather than attempting to catch missing attributes on tags.
- This is because the target of the testing framework is the PHP application
- logic, not syntax or other errors in the HTML code.
- For HTML errors, other tools such as
- HTMLTidy should be used.
-
-
- If a field is not present in any form, or if an option is unavailable,
- then WebTestCase::setField() will return
- false.
- For example, suppose we wish to verify that a "Superuser"
- option is not present in this form...
-
-<strong>Select type of user to add:</strong>
-<select name="type">
- <option>Subscriber</option>
- <option>Author</option>
- <option>Administrator</option>
-</select>
-
- The selection will not be changed on a failure to set
- a widget value.
-
-
- Here is the full list of widgets currently supported...
-
-
Text fields, including hidden and password fields.
-
Submit buttons including the button tag, although not yet reset buttons
-
Text area. This includes text wrapping behaviour.
-
Checkboxes, including multiple checkboxes in the same form.
-
Drop down selections, including multiple selects.
-
Radio buttons.
-
Images.
-
-
-
- Although most standard HTML widgets are catered for by SimpleTest's
- built in parser, it is unlikely that JavaScript will be implemented
- anytime soon.
-
- 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...
-
- 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.
-
-
- 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.
-
- As many cases as needed can appear in a single file.
- They should include any code they need, such as the library
- being tested, but none of the simple test libraries.
-
-
- If you have extended any test cases, you can include them
- as well.
-
- The FileTester class does
- not contain any actual tests, but is a base class for other
- test cases.
- For this reason we use the
- SimpleTestOptions::ignore() directive
- to tell the upcoming group test to ignore it.
- This directive can appear anywhere in the file and works
- when a whole file of test cases is loaded (see below).
- We will call this sample file_test.php.
-
-
- Next we create a group test file, called say group_test.php.
- You will think of a better name I am sure.
- We will add the test file using a safe method...
-
- This instantiates the test case before the test suite is
- run.
- This could get a little expensive with a large number of test
- cases, so another method is provided that will only
- instantiate the class when it is needed...
-
- The problem with this method is that for every test case
- that we add we will have
- to require_once() the test code
- file and manually instantiate each and every test case.
- We can save a lot of typing with...
-
- What happens here is that the GroupTest
- class has done the require_once()
- for us.
- It then checks to see if any new test case classes
- have been created by the new file and automatically adds
- them to the group test.
- Now all we have to do is add each new file.
-
-
- There are two things that could go wrong and which require care...
-
-
- The file could already have been parsed by PHP and so no
- new classes will have been added. You should make
- sure that the test cases are only included in this file
- and no others.
-
-
- New test case extension classes that get included will be
- placed in the group test and run also.
- You will need to add a SimpleTestOptions::ignore()
- directive for these classes or make sure that they are included
- before the GroupTest::addTestFile()
- line.
-
- The above method places all of the test cases into one large group.
- For larger projects though this may not be flexible enough; you
- may want to group the tests in all sorts of ways.
-
-
- To get a more flexible group test we can subclass
- GroupTest and then instantiate it as needed...
-
- This effectively names the test in the constructor and then
- adds our test cases and a single group below.
- Of course we can add more than one group at this point.
- We can now invoke the tests from a separate runner file...
-
- If we still wish to run the original group test and we
- don't want all of these little runner files, we can
- put the test runner code around guard bars when we create
- each group.
-
- This approach requires the guard to be set when including
- the group test file, but this is still less hassle than
- lots of separate runner files.
- You include the same guard on the top level tests to make sure
- that run() will run once only
- from the top level script that has been invoked.
-
- If you already have unit tests for your code or are extending external
- classes that have tests, it is unlikely that all of the test cases
- are in SimpleTest format.
- Fortunately it is possible to incorporate test cases from other
- unit testers directly into SimpleTest group tests.
-
-
- Say we have the following
- PhpUnit
- test case in the file config_test.php...
-
- The PEAR test cases can be freely mixed with SimpleTest
- ones even in the same test file,
- but you cannot use SimpleTest assertions in the legacy
- test case versions.
- This is done as a check that you are not accidently making
- your test cases completely dependent on SimpleTest.
- You may want to do a PEAR release of your library for example
- which would mean shipping it with valid PEAR::PhpUnit test
- cases.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
-
-
diff --git a/tests/UnitTests/simpletest/docs/en/index.html b/tests/UnitTests/simpletest/docs/en/index.html
deleted file mode 100644
index d5a30d31..00000000
--- a/tests/UnitTests/simpletest/docs/en/index.html
+++ /dev/null
@@ -1,467 +0,0 @@
-
-
-
-
- Download the Simple Test testing framework -
- Unit tests and mock objects for PHP
-
-
-
-
-
- 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
- SimpleTest.
- For fuller documentation, especially if you are new
- to unit testing see the ongoing
- documentation, and for
- example test cases see the
- unit testing tutorial.
-
- 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
- JUnit and the
- PHPUnit
- ports, but also adds
- mock objects.
- It has some JWebUnit
- functionality as well.
- This includes web page navigation, cookie testing and form submission.
-
-
- The quickest way to demonstrate is with an example.
-
-
- Let us suppose we are testing a simple file logging class called
- Log in classes/log.php.
- We start by creating a test script which we will call
- tests/log_test.php and populate it as follows...
-
- Here the simpletest folder is either local or in the path.
- You would have to edit these locations depending on where you
- placed the toolset.
- Next we create a test case...
-
- Now we have five lines of scaffolding code and still no tests.
- However from this part on we get return on our investment very quickly.
- We'll assume that the Log class
- takes the file name to write to in the constructor and we have
- a temporary folder in which to place this file...
-
-<?php
-require_once('simpletest/unit_tester.php');
-require_once('simpletest/reporter.php');
-require_once('../classes/log.php');
-
-class TestOfLogging extends UnitTestCase {
-
- function testCreatingNewFile() {
- @unlink('/temp/test.log');
- $log = new Log('/temp/test.log');
- $this->assertFalse(file_exists('/temp/test.log'));
- $log->message('Should write this to a file');
- $this->assertTrue(file_exists('/temp/test.log'));
- }
-}
-?>
-
- When a test case runs it will search for any method that
- starts with the string test
- and execute that method.
- We would normally have more than one test method of course.
- Assertions within the test methods trigger messages to the
- test framework which displays the result immediately.
- This immediate response is important, not just in the event
- of the code causing a crash, but also so that
- print statements can display
- their content right next to the test case concerned.
-
-
- To see these results we have to actually run the tests.
- If this is the only test case we wish to run we can achieve
- it with...
-
-<?php
-require_once('simpletest/unit_tester.php');
-require_once('simpletest/reporter.php');
-require_once('../classes/log.php');
-
-class TestOfLogging extends UnitTestCase {
-
- function testCreatingNewFile() {
- @unlink('/temp/test.log');
- $log = new Log('/temp/test.log');
- $this->assertFalse(file_exists('/temp/test.log'));
- $log->message('Should write this to a file');
- $this->assertTrue(file_exists('/temp/test.log'));
- }
-}
-
-$test = &new TestOfLogging();
-$test->run(new HtmlReporter());
-?>
-
- Fatal error: Failed opening required '../classes/log.php' (include_path='') in /home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php on line 7
-
- it means you're missing the classes/Log.php file that could look like...
-
- It is unlikely in a real application that we will only ever run
- one test case.
- This means that we need a way of grouping cases into a test
- script that can, if need be, run every test in the application.
-
-
- Our first step is to strip the includes and to undo our
- previous hack...
-
-<?php
-require_once('../classes/log.php');
-
-class TestOfLogging extends UnitTestCase {
-
- function testCreatingNewFile() {
- @unlink('/temp/test.log');
- $log = new Log('/temp/test.log');
- $this->assertFalse(file_exists('/temp/test.log'));
- $log->message('Should write this to a file');
- $this->assertTrue(file_exists('/temp/test.log'));
- }
-}
-?>
-
- Next we create a new file called tests/all_tests.php
- and insert the following code...
-
- The method GroupTest::addTestFile()
- will include the test case file and read any new classes created
- that are descended from SimpleTestCase, of which
- UnitTestCase is one example.
- Just the class names are stored for now, so that the test runner
- can instantiate the class when it works its way
- through your test suite.
-
-
- For this to work properly the test case file should not blindly include
- any other test case extensions that do not actually run tests.
- This could result in extra test cases being counted during the test
- run.
- Hardly a major problem, but to avoid this inconvenience simply add
- a SimpleTestOptions::ignore() directive
- somewhere in the test case file.
- Also the test case file should not have been included
- elsewhere or no cases will be added to this group test.
- This would be a more serious error as if the test case classes are
- already loaded by PHP the GroupTest::addTestFile()
- method will not detect them.
-
-
- To display the results it is necessary only to invoke
- tests/all_tests.php from the web server.
-
- 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
- SessionPool.
- We want to test a method that will probably end up looking
- like this...
-
- This test case design is not all bad, but it could be improved.
- We are spending time fiddling with log files which are
- not part of our test. Worse, we have created close ties
- with the Log class and
- this test.
- What if we don't use files any more, but use ths
- syslog library instead?
- Did you notice the extra carriage return in the message?
- Was that added by the logger?
- What if it also added a time stamp or other data?
-
-
- The only part that we really want to test is that a particular
- message was sent to the logger.
- We reduce coupling if we can pass in a fake logging class
- that simply records the message calls for testing, but
- takes no action.
- It would have to look exactly like our original though.
-
-
- 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.
-
-
- Too good to be true?
- Luckily we can create such an object easily...
-
- The tally() call is needed to
- tell the mock object that time is up for the expected call
- count.
- Without it the mock would wait forever for the method
- call to come in without ever actually notifying the test case.
- The other test will be triggered when the call to
- message() is invoked on the
- MockLog object.
- The mock call will trigger a parameter comparison and then send the
- resulting pass or fail event to the test display.
- Wildcards can be included here too so as to prevent tests
- becoming too specific.
-
-
- 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.
-
-
- For this test to run the mock objects library must have been
- included in the test suite, say in all_tests.php.
-
- 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.
-
-
- The web testing in SimpleTest is fairly primitive, there is
- no JavaScript for example.
- To give an idea here is a trivial example where a home
- page is fetched, from which we navigate to an "about"
- page and then test some client determined content.
-
-<?php
-require_once('simpletest/web_tester.php');
-require_once('simpletest/reporter.php');
-
-class TestOfAbout extends WebTestCase {
-
- function setUp() {
- $this->get('http://test-server/index.php');
- $this->clickLink('About');
- }
-
- function testSearchEngineOptimisations() {
- $this->assertTitle('A long title about us for search engines');
- $this->assertWantedPattern('/a popular keyphrase/i');
- }
-}
-$test = &new TestOfAbout();
-$test->run(new HtmlReporter());
-?>
-
- 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.
-
-
-
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
- Mock objects have two roles during a test case: actor and critic.
-
-
- 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.
-
-
- If mock objects only behaved as actors they would simply be
- known as server stubs.
-
-
- However, the mock objects not only play a part (by supplying chosen
- return values on demand) they are also sensitive to the
- messages sent to them (via expectations).
- By setting expected parameters for a method call they act
- as a guard that the calls upon them are made correctly.
- If expectations are not met they save us the effort of
- writing a failed test assertion by performing that duty on our
- behalf.
- In the case of an imaginary database connection they can
- test that the query, say SQL, was correctly formed by
- the object that is using the connection.
- Set them up with fairly tight expectations and you will
- hardly need manual assertions at all.
-
- In the same way that we create server stubs, all we need is an
- existing class, say a database connection that looks like this...
-
-class DatabaseConnection {
- function DatabaseConnection() {
- }
-
- function query() {
- }
-
- function selectQuery() {
- }
-}
-
- The class does not need to have been implemented yet.
- To create a mock version of the class we need to include the
- mock object library and run the generator...
-
- Unlike the generated stubs the mock constructor needs a reference
- to the test case so that it can dispatch passes and failures while
- checking its expectations.
- This means that mock objects can only be used within test cases.
- Despite this their extra power means that stubs are hardly ever used
- if mocks are available.
-
-
- The mock version of a class has all the methods of the original
- so that operations like
- $connection->query() are still
- legal.
- As with stubs we can replace the default null return values...
-
-$connection->setReturnValue('query', 37);
-
- Now every time we call
- $connection->query() we get
- the result of 37.
- As with the stubs we can set wildcards and we can overload the
- wildcard parameter.
- We can also add extra methods to the mock when generating it
- and choose our own class name...
-
- Here the mock will behave as if the setOptions()
- existed in the original class.
- This is handy if a class has used the PHP overload()
- mechanism to add dynamic methods.
- You can create a special mock to simulate this situation.
-
-
- All of the patterns available with server stubs are available
- to mock objects...
-
-class Iterator {
- function Iterator() {
- }
-
- function next() {
- }
-}
-
- Again, assuming that this iterator only returns text until it
- reaches the end, when it returns false, we can simulate it
- with...
-
- When next() is called on the
- mock iterator it will first return "First string",
- on the second call "Second string" will be returned
- and on any other call false 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.
-
-
- A repeat of the stubbed information holder with name/value pairs...
-
-class Configuration {
- function Configuration() {
- }
-
- function getValue($key) {
- }
-}
-
- This is a classic situation for using mock objects as
- actual configuration will vary from machine to machine,
- hardly helping the reliability of our tests if we use it
- directly.
- The problem though is that all the data comes through the
- getValue() method and yet
- we want different results for different keys.
- Luckily the mocks have a filter system...
-
- 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
- getValue() method invoked
- like this...
-
-$config->getValue('db_user')
-
- ...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.
-
-
- There are times when you want a specific object to be
- dished out by the mock rather than a copy.
- Again this is identical to the server stubs mechanism...
-
- 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.
-
-
- By way of example, suppose we have a
- SessionPool class that we
- want to add logging to.
- Rather than grow the original class into something more
- complicated, we want to add this behaviour with a decorator (GOF).
- The SessionPool code currently looks
- like this...
-
- Out of all of this, the only class we want to test here
- is the LoggingSessionPool.
- In particular we would like to check that the
- findSession() method is
- called with the correct session ID in the cookie and that
- it sent the message "Starting session $cookie"
- to the logger.
-
-
- Despite the fact that we are testing only a few lines of
- production code, here is what we would have to do in a
- conventional test case:
-
-
Create a log object.
-
Set a directory to place the log file.
-
Set the directory permissions so we can write the log.
-
Create a SessionPool object.
-
Hand start a session, which probably does lot's of things.
-
Invoke findSession().
-
Read the new Session ID (hope there is an accessor!).
-
Raise a test assertion to confirm that the ID matches the cookie.
-
Read the last line of the log file.
-
Pattern match out the extra logging timestamps, etc.
-
Assert that the session message is contained in the text.
-
- It is hardly surprising that developers hate writing tests
- when they are this much drudgery.
- To make things worse, every time the logging format changes or
- the method of creating new sessions changes, we have to rewrite
- parts of this test even though this test does not officially
- test those parts of the system.
- We are creating headaches for the writers of these other classes.
-
-
- Instead, here is the complete test method using mock object magic...
-
- We start by creating a dummy session.
- We don't have to be too fussy about this as the check
- for which session we want is done elsewhere.
- We only need to check that it was the same one that came
- from the session pool.
-
-
- findSession() is a factory
- method the simulation of which is described above.
- The point of departure comes with the first
- expectOnce() call.
- This line states that whenever
- findSession() is invoked on the
- mock, it will test the incoming arguments.
- If it receives the single argument of a string "abc"
- then a test pass is sent to the unit tester, otherwise a fail is
- generated.
- This was the part where we checked that the right session was asked for.
- The argument list follows the same format as the one for setting
- return values.
- You can have wildcards and sequences and the order of
- evaluation is the same.
-
-
- If the call is never made then neither a pass nor a failure will
- generated.
- To get around this we must tell the mock when the test is over
- so that the object can decide if the expectation has been met.
- The unit tester assertion for this is triggered by the
- tally() call at the end of
- the test.
-
-
- We use the same pattern to set up the mock logger.
- We tell it that it should have
- message() invoked
- once only with the argument "Starting session abc".
- By testing the calling arguments, rather than the logger output,
- we insulate the test from any display changes in the logger.
-
-
- We start to run our tests when we create the new
- LoggingSessionPool and feed
- it our preset mock objects.
- Everything is now under our control.
- Finally we confirm that the
- $session we gave our decorator
- is the one that we get back and tell the mocks to run their
- internal call count tests with the
- tally() calls.
-
-
- This is still quite a bit of test code, but the code is very
- strict.
- If it still seems rather daunting there is a lot less of it
- than if we tried this without mocks and this particular test,
- interactions rather than output, is always more work to set
- up.
- More often you will be testing more complex situations without
- needing this level or precision.
- Also some of this can be refactored into a test case
- setUp() method.
-
-
- Here is the full list of expectations you can set on a mock object
- in SimpleTest...
-
-
-
-
Expectation
Needs tally()
-
-
-
-
-
expectArguments($method, $args)
-
No
-
-
-
expectArgumentsAt($timing, $method, $args)
-
No
-
-
-
expectCallCount($method, $count)
-
Yes
-
-
-
expectMaximumCallCount($method, $count)
-
No
-
-
-
expectMinimumCallCount($method, $count)
-
Yes
-
-
-
expectNever($method)
-
No
-
-
-
expectOnce($method, $args)
-
Yes
-
-
-
expectAtLeastOnce($method, $args)
-
Yes
-
-
-
- Where the parameters are...
-
-
$method
-
The method name, as a string, to apply the condition to.
-
$args
-
- The arguments as a list. Wildcards can be included in the same
- manner as for setReturn().
- This argument is optional for expectOnce()
- and expectAtLeastOnce().
-
-
$timing
-
- The only point in time to test the condition.
- The first call starts at zero.
-
-
$count
-
The number of calls expected.
-
- The method expectMaximumCallCount()
- is slightly different in that it will only ever generate a failure.
- It is silent if the limit is never reached.
-
-
- 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".
-
- There are three approaches to creating mocks including the one
- that SimpleTest employs.
- Coding them by hand using a base class, generating them to
- a file and dynamically generating them on the fly.
-
-
- Mock objects generated with SimpleTest
- are dynamic.
- They are created at run time in memory, using
- eval(), rather than written
- out to a file.
- This makes the mocks easy to create, a one liner,
- especially compared with hand
- crafting them in a parallel class hierarchy.
- The problem is that the behaviour is usually set up in the tests
- themselves.
- If the original objects change the mock versions
- that the tests rely on can get out of sync.
- This can happen with the parallel hierarchy approach as well,
- but is far more quickly detected.
-
-
- The solution, of course, is to add some real integration
- tests.
- You don't need very many and the convenience gained
- from the mocks more than outweighs the small amount of
- extra testing.
- You cannot trust code that was only tested with mocks.
-
-
- If you are still determined to build static libraries of mocks
- because you want to simulate very specific behaviour, you can
- achieve the same effect using the SimpleTest class generator.
- In your library file, say mocks/connection.php for a
- database connection, create a mock and inherit to override
- special methods or add presets...
-
- The generate call tells the class generator to create
- a class called BasicMockConnection
- rather than the usual MockConnection.
- We then inherit from this to get our version of
- MockConnection.
- By intercepting in this way we can add behaviour, here setting
- the default value of query() to be false.
- By using the default name we make sure that the mock class
- generator will not recreate a different one when invoked elsewhere in the
- tests.
- It never creates a class if it already exists.
- As long as the above file is included first then all tests
- that generated MockConnection should
- now be using our one instead.
- If we don't get the order right and the mock library
- creates one first then the class creation will simply fail.
-
-
- Use this trick if you find you have a lot of common mock behaviour
- or you are getting frequent integration problems at later
- stages of testing.
-
- But at the time of writing it is the only one with mock objects,
- so are you stuck with it?
-
-
- No, not at all.
- SimpleTest is a toolkit and one of those
- tools is the mock objects which can be employed independently.
- Suppose you have your own favourite unit tester and all your current
- test cases are written using it.
- Pretend that you have called your unit tester PHPUnit (everyone else has)
- and the core test class looks like this...
-
- All the assertion() method does
- is print some fancy output and the boolean assertion parameter determines
- whether to print a pass or a failure.
- Let's say that it is used like this...
-
-$unit_test = new PHPUnit();
-$unit_test>assertion('I hope this file exists', file_exists('my_file'));
-
- How do you use mocks with this?
-
-
- There is a protected method on the base mock class
- SimpleMock called
- _assertTrue() and
- by overriding this method we can use our own assertion format.
- We start with a subclass, in say my_mock.php...
-
- Now instantiating MyMock will create
- an object that speaks the same language as your tester.
- The catch is of course that we never create such an object, the
- code generator does.
- We need just one more line of code to tell the generator to use
- your mock instead...
-
- From now on you just include my_mock.php instead of the
- default mock_objects.php version and you can introduce
- mock objects into your existing test suite.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
-
-
diff --git a/tests/UnitTests/simpletest/docs/en/overview.html b/tests/UnitTests/simpletest/docs/en/overview.html
deleted file mode 100644
index 5047598d..00000000
--- a/tests/UnitTests/simpletest/docs/en/overview.html
+++ /dev/null
@@ -1,422 +0,0 @@
-
-
-
-
- Overview and feature list for the SimpleTest PHP unit tester and web tester
-
-
-
-
-
- The heart of SimpleTest is a testing framework built around
- test case classes.
- These are written as extensions of base test case classes,
- each extended with methods that actually contain test code.
- Top level test scripts then invoke the run()
- methods on every one of these test cases in order.
- Each test method is written to invoke various assertions that
- the developer expects to be true such as
- assertEqual().
- If the expectation is correct, then a successful result is dispatched to the
- observing test reporter, but any failure triggers an alert
- and a description of the mismatch.
-
- These tools are designed for the developer.
- Tests are written in the PHP language itself more or less
- as the application itself is built.
- The advantage of using PHP itself as the testing language is that
- there are no new languages to learn, testing can start straight away,
- and the developer can test any part of the code.
- Basically, all parts that can be accessed by the application code can also be
- accessed by the test code if they are in the same language.
-
-
- The simplest type of test case is the
- UnitTestCase.
- 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.
-
-
- 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 WebTestCase 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.
-
- The following is a very rough outline of past and future features
- and their expected point of release.
- I am afraid it is liable to change without warning as meeting the
- milestones rather depends on time available.
- Green stuff has been coded, but not necessarily released yet.
- If you have a pressing need for a green but unreleased feature
- then you should check-out the code from sourceforge CVS directly.
- A released feature is marked as "Done".
-
-
-
-
Feature
Description
Release
-
-
-
-
-
Unit test case
-
Core test case class and assertions
-
Done
-
-
-
Html display
-
Simplest possible display
-
Done
-
-
-
Autoloading of test cases
-
- Reading a file with test cases and loading them into a
- group test automatically
-
-
Done
-
-
-
Mock objects code generator
-
- Objects capable of simulating other objects removing
- test dependencies
-
-
Done
-
-
-
Server stubs
-
- Mocks without expectations to be used outside of test cases,
- e.g. for prototyping
-
-
Done
-
-
-
Integration of other unit testers
-
- The ability to read and simulate test cases from PHPUnit
- and PEAR::PhpUnit
-
-
Done
-
-
-
Web test case
-
Basic pattern matching of fetched pages
-
Done
-
-
-
HTML parsing of pages
-
Allows link following and title tag matching
-
Done
-
-
-
Partial mocks
-
- Mocking parts of a class for testing less than a class
- or for complex simulations
-
-
Done
-
-
-
Web cookie handling
-
Correct handling of cookies when fetching pages
-
Done
-
-
-
Following redirects
-
Page fetching automatically follows 300 redirects
-
Done
-
-
-
Form parsing
-
Ability to submit simple forms and read default form values
-
Done
-
-
-
Command line interface
-
Test display without the need of a web browser
-
Done
-
-
-
Exposure of expectation classes
-
Can create precise tests with mocks as well as test cases
-
Done
-
-
-
XML output and parsing
-
- Allows multi host testing and the integration of acceptance
- testing extensions
-
-
Done
-
-
-
Command line test case
-
Allows testing of utilities and file handling
-
Done
-
-
-
PHP Documentor compatibility
-
Fully generated class level documentation
-
Done
-
-
-
Browser interface
-
- Exposure of lower level web browser interface for more
- detailed test cases
-
-
Done
-
-
-
HTTP authentication
-
- Fetching protected web pages with basic authentication
- only
-
-
Done
-
-
-
Browser navigation buttons
-
Back, forward and retry
-
Done
-
-
-
SSL support
-
Can connect to https: pages
-
Done
-
-
-
Proxy support
-
Can connect via. common proxies
-
Done
-
-
-
Frames support
-
Handling of frames in web test cases
-
Done
-
-
-
Improved display
-
Better web GUI with tree display of test cases
-
1.1
-
-
-
Localisation
-
Messages abstracted and code generated from XML
-
1.1
-
-
-
File upload testing
-
Can simulate the input type file tag
-
1.1
-
-
-
Mocking interfaces
-
Can generate mock objects to interfaces as well as classes
-
2.0
-
-
-
Testing exceptions
-
Similar to testing PHP errors
-
2.0
-
-
-
XPath searching of elements
-
Can make use of HTML tidy for faster and more flexible content matching
-
2.0
-
-
-
- PHP5 migraton will start straight after the version 1.1 series,
- whereupon PHP4 will no longer be supported.
- SimpleTest is currently compatible with PHP5, but will not
- make use of all of the new features until version 2.
-
-
-
- Process is at least as important as tools.
- The type of process that makes the heaviest use of a developer's
- testing tool is of course
- Extreme Programming.
- This is one of the
- Agile Methodologies
- which combine various practices to "flatten the cost curve" of software development.
- More extreme still is Test Driven Development,
- where you very strictly adhere to the rule of no coding until you have a test.
- If you're more of a planner or believe that experience trumps evolution,
- you may prefer the
- RUP approach.
- I haven't tried it, but even I can see that you will need test tools (see figure 9).
-
-
- Most unit testers clone JUnit to some degree,
- as far as the interface at least. There is a wealth of information on the
- JUnit site including the
- FAQ
- which contains plenty of general advice on testing.
- Once you get bitten by the bug you will certainly appreciate the phrase
- test infected
- coined by Eric Gamma.
- If you are still reviewing which unit tester to use the main choices
- are PHPUnit
- and Pear PHP::PHPUnit.
- They currently lack a lot of features found in
- SimpleTest, but the PEAR
- version at least has been upgraded for PHP5 and is recommended if you are porting
- existing JUnit test cases.
-
-
- Library writers don't seem to ship tests with their code very often
- which is a shame.
- Library code that includes tests can be more safely refactored and
- the test code can act as additional documentation in a fairly standard
- form.
- This can save trawling the source code for clues when problems occour,
- especially when upgrading such a library.
- Libraries using SimpleTest for their unit testing include
- WACT and
- PEAR::XML_HTMLSax.
-
-
- There is currently a sad lack of material on mock objects, which is a shame
- as unit testing without them is a lot more work.
- The original mock objects paper
- is very Java focused, but still worth a read.
- As a new technology there are plenty of discussions and debate on how to use mocks,
- often on Wikis such as
- Extreme Tuesday
- or www.mockobjects.com
- or the original C2 Wiki.
- Injecting mocks into a class is the main area of debate for which this
- paper on IBM
- makes a good starting point.
-
-
- There are plenty of web testing tools, but most are written in Java and
- tutorials and advice are rather thin on the ground.
- The only hope is to look at the documentation for
- HTTPUnit,
- HTMLUnit
- or JWebUnit and hope for clues.
- There are some XML driven test frameworks, but again most
- require Java to run.
- As SimpleTest does not support JavaScript you would probably
- have to look at these tools anyway if you have highly dynamic
- pages.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
- 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.
-
- 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.
-
-
- 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...
-
- We would really like to have a mock object version of the socket
- here, what can we do?
-
-
- 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.
-
- 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.
-
-
- The next simplest compromise is to have the created object passed
- in as an optional parameter...
-
- 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.
-
-
- The next method is to pass in a factory object to do the creation...
-
- 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...
-
- 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.
-
-
- 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...
-
- 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
- connect().
- Otherwise it could get a null value from
- _createSocket().
-
-
- After the completion of all of this extra work the
- actual test case is fairly easy.
- We just test our new class instead...
-
- 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?
-
-
-
- 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.
-
-
- Here is the partial mock version of the test...
-
- The partial mock is a subclass of the original with
- selected methods "knocked out" with test
- versions.
- The generatePartial() call
- takes three parameters: the class to be subclassed,
- the new test class name and a list of methods to mock.
-
-
- 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.
-
-
- 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.
-
-
- 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...
-
- 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.
-
-
- This last situation is all rather hypothetical, as I haven't
- tried it.
- I am open to the possibility, but a little worried that
- forcing object granularity may be better for the code quality.
- I personally use partial mocks as a way of overriding creation
- or for occasional testing of the TemplateMethod pattern.
-
-
- It's all going to come down to the coding standards of your
- project to decide which mechanism you use.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
-
-
diff --git a/tests/UnitTests/simpletest/docs/en/reporter_documentation.html b/tests/UnitTests/simpletest/docs/en/reporter_documentation.html
deleted file mode 100644
index 2794c5ab..00000000
--- a/tests/UnitTests/simpletest/docs/en/reporter_documentation.html
+++ /dev/null
@@ -1,515 +0,0 @@
-
-
-
-SimpleTest for PHP test runner and display documentation
-
-
-
-
- SimpleTest pretty much follows the MVC pattern
- (Model-View-Controller).
- The reporter classes are the view and the model is your
- test cases and their hiearchy.
- The controller is mostly hidden from the user of
- SimpleTest unless you want to change how the test cases
- are actually run, in which case it is possible to
- override the runner objects from within the test case.
- As usual with MVC, the controller is mostly undefined
- and there are other places to control the test run.
-
- The default test display is minimal in the extreme.
- It reports success and failure with the conventional red and
- green bars and shows a breadcrumb trail of test groups
- for every failed assertion.
- Here's a fail...
-
-
File test
- Fail: createnewfile->True assertion failed.
-
1/1 test cases complete.
- 0 passes, 1 fails and 0 exceptions.
-
- And here all tests passed...
-
-
File test
-
1/1 test cases complete.
- 1 passes, 0 fails and 0 exceptions.
-
- The good news is that there are several points in the display
- hiearchy for subclassing.
-
-
- For web page based displays there is the
- HtmlReporter class with the following
- signature...
-
-class HtmlReporter extends SimpleReporter {
- public HtmlReporter($encoding) { ... }
- public makeDry(boolean $is_dry) { ... }
- public void paintHeader(string $test_name) { ... }
- public void sendNoCacheHeaders() { ... }
- public void paintFooter(string $test_name) { ... }
- public void paintGroupStart(string $test_name, integer $size) { ... }
- public void paintGroupEnd(string $test_name) { ... }
- public void paintCaseStart(string $test_name) { ... }
- public void paintCaseEnd(string $test_name) { ... }
- public void paintMethodStart(string $test_name) { ... }
- public void paintMethodEnd(string $test_name) { ... }
- public void paintFail(string $message) { ... }
- public void paintPass(string $message) { ... }
- public void paintError(string $message) { ... }
- public void paintException(string $message) { ... }
- public void paintMessage(string $message) { ... }
- public void paintFormattedMessage(string $message) { ... }
- protected string _getCss() { ... }
- public array getTestList() { ... }
- public integer getPassCount() { ... }
- public integer getFailCount() { ... }
- public integer getExceptionCount() { ... }
- public integer getTestCaseCount() { ... }
- public integer getTestCaseProgress() { ... }
-}
-
- Here is what some of these methods mean. First the display methods
- that you will probably want to override...
-
-
- HtmlReporter(string $encoding)
-
- 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 html_entities()
- function.
-
-
- void paintHeader(string $test_name)
-
- 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 $test_name
- comes from.
- It paints the page titles, CSS, body tag, etc.
- It returns nothing (void).
-
-
- void paintFooter(string $test_name)
-
- 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.
-
-
- void paintMethodStart(string $test_name)
-
- 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.
-
-
- void paintMethodEnd(string $test_name)
-
- backs out of the test started with the same name.
-
-
- void paintFail(string $message)
-
- 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.
-
-
- void paintPass(string $message)
-
- by default does nothing.
-
-
- string _getCss()
-
- 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.
-
-
- There are also some accessors to get information on the current
- state of the test suite.
- Use these to enrich the display...
-
-
- array getTestList()
-
- is the first convenience method for subclasses.
- Lists the current nesting of the tests as a list
- of test names.
- The first, most deeply nested test, is first in the
- list and the current test method will be last.
-
-
- integer getPassCount()
-
- returns the number of passes chalked up so far.
- Needed for the display at the end.
-
-
- integer getFailCount()
-
- is likewise the number of fails so far.
-
-
- integer getExceptionCount()
-
- is likewise the number of errors so far.
-
-
- integer getTestCaseCount()
-
- is the total number of test cases in the test run.
- This includes the grouping tests themselves.
-
-
- integer getTestCaseProgress()
-
- is the number of test cases completed so far.
-
-
- One simple modification is to get the HtmlReporter to display
- the passes as well as the failures and errors...
-
- One method that was glossed over was the makeDry()
- 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.
-
-
- 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.
-
- 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
- HtmlReporter we can take one
- step up the class hiearchy to SimpleReporter
- in the simple_test.php source file.
-
-
- A do nothing display, a blank canvas for your own creation, would
- be...
-
- SimpleTest also ships with a minimal command line reporter.
- The interface mimics JUnit to some extent, but paints the
- failure messages as they arrive.
- To use the command line reporter simply substitute it
- for the HTML version...
-
-File test
-1) True assertion failed.
- in createnewfile
-FAILURES!!!
-Test cases run: 1/1, Failures: 1, Exceptions: 0
-
-
-
- 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 false
- is returned from the SimpleTest::run()
- method.
- We can use that result to exit the script with the desired return
- code...
-
- Of course we don't really want to create two test scripts,
- a command line one and a web browser one, for each test suite.
- The command line reporter includes a method to sniff out the
- run time environment...
-
- You can make use of this format with the parser
- supplied as part of SimpleTest itself.
- This is called SimpleTestXmlParser and
- resides in xml.php within the SimpleTest package...
-
- The $test_output 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.
-
-
- A problem with large test suites is thet they can exhaust
- the default 8Mb memory limit on a PHP process.
- By having the test groups output in XML and run in
- separate processes, the output can be reparsed to
- aggregate the results into a much smaller footprint top level
- test.
-
-
- 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...
-
- The RemoteTestCase 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 RemoteTestCase can be added to test suites
- just like any other group test.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-
- This was originally a pattern named by Robert Binder (Testing
- object-oriented systems: models, patterns, and tools,
- Addison-Wesley) in 1999.
- A server stub is a simulation of an object or component.
- It should exactly replace a component in a system for test
- or prototyping purposes, but remain lightweight.
- This allows tests to run more quickly, or if the simulated
- class has not been written, to run at all.
-
- All we need is an existing class, say a database connection
- that looks like this...
-
-class DatabaseConnection {
- function DatabaseConnection() {
- }
-
- function query() {
- }
-
- function selectQuery() {
- }
-}
-
- The class does not need to have been implemented yet.
- To create a stub version of the class we need to include the
- server stub library and run the generator...
-
- This generates a clone class called
- StubDatabaseConnection.
- We can now create instances of the new class within
- our prototype script...
-
-require_once('simpletest/mock_objects.php');
-require_once('database_connection.php');
-Stub::generate('DatabaseConnection');
-
-$connection = new StubDatabaseConnection();
-
-
- The stub version of a class has all the methods of the original
- so that operations like
- $connection->query() are still
- legal.
- The return value will be null,
- but we can change that with...
-
-$connection->setReturnValue('query', 37)
-
- Now every time we call
- $connection->query() we get
- the result of 37.
- We can set the return value to anything, say a hash of
- imaginary database results or a list of persistent objects.
- Parameters are irrelevant here, we always get the same
- values back each time once they have been set up this way.
- That may not sound like a convincing replica of a
- database connection, but for the half a dozen lines of
- a test method it is usually all you need.
-
-
-
- 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...
-
-class Iterator {
- function Iterator() {
- }
-
- function next() {
- }
-}
-
- 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...
-
- When next() is called on the
- stub iterator it will first return "First string",
- on the second call "Second string" will be returned
- and on any other call false 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.
-
-
- Another tricky situation is an overloaded
- get() operation.
- An example of this is an information holder with name/value pairs.
- Say we have a configuration class like...
-
-class Configuration {
- function Configuration() {
- }
-
- function getValue($key) {
- }
-}
-
- This is a classic situation for using stub objects as
- actual configuration will vary from machine to machine,
- hardly helping the reliability of our tests if we use it
- directly.
- The problem though is that all the data comes through the
- getValue() method and yet
- we want different results for different keys.
- Luckily the stubs have a filter system...
-
- The extra parameter is a list of arguments to attempt
- to match.
- In this case we are trying to match only one argument which
- is the look up key.
- Now when the server stub has the
- getValue() method invoked
- like this...
-
-$config->getValue('db_user');
-
- ...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.
-
-
- You can set a default argument argument like so...
-
- This is not the same as setting the return value without
- any argument requirements like this...
-
-
-$config->setReturnValue('getValue', false);
-
- 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.
-
-
- There are times when you want a specific object to be
- dished out by the stub rather than just a copy.
- The PHP copy semantics force us to use a different method
- for this.
- You might be simulating a container for example...
-
- This will return the $stuff only on the third
- call and only if two parameters were set the second of
- which must be the integer 1.
- That should cover most simple prototyping situations.
-
-
- 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 next() giving
- one row until false.
- This sounds like a simulation nightmare, but in fact it can all
- be stubbed using the mechanics above.
-
- Now only if our
- $connection is called with the correct
- query() will the
- $result be returned that is
- itself exhausted after the third call to next().
- This should be enough
- information for our UserFinder class,
- the class actually
- being tested here, to come up with goods.
- A very precise test and not a real database in sight.
-
-
-
- This is not very useful in itself as there would be no difference
- in this class and the default except for the name.
- However we can also add additional methods not found in the
- original interface...
-
- The core system is a regression testing framework built around
- test cases.
- A sample test case looks like this...
-
-class FileTestCase extends UnitTestCase {
-}
-
- If no test name is supplied when chaining the constructor then
- the class name will be taken instead.
- This will be the name displayed in the test results.
-
-
- Actual tests are added as methods in the test case whose names
- by default start with the string "test" and
- when the test case is invoked all such methods are run in
- the order that PHP introspection finds them.
- As many test methods can be added as needed.
- For example...
-
- The constructor is optional and usually omitted.
- Without a name, the class name is taken as the name of the test case.
-
-
- Our only test method at the moment is testCreation()
- where we check that a file has been created by our
- Writer object.
- We could have put the unlink()
- code into this method as well, but by placing it in
- setUp() and
- tearDown() we can use it with
- other test methods that we add.
-
-
- The setUp() method is run
- just before each and every test method.
- tearDown() is run just after
- each and every test method.
-
-
- You can place some test case set up into the constructor to
- be run once for all the methods in the test case, but
- you risk test inteference that way.
- This way is slightly slower, but it is safer.
- Note that if you come from a JUnit background this will not
- be the behaviour you are used to.
- JUnit surprisingly reinstantiates the test case for each test
- method to prevent such interference.
- SimpleTest requires the end user to use setUp(), but
- supplies additional hooks for library writers.
-
-
- The means of reporting test results (see below) are by a
- visiting display class
- that is notified by various assert...()
- methods.
- Here is the full list for the UnitTestCase
- class, the default for SimpleTest...
-
-
-
-
assertTrue($x)
Fail if $x is false
-
-
-
assertFalse($x)
Fail if $x is true
-
-
-
assertNull($x)
Fail if $x is set
-
-
-
assertNotNull($x)
Fail if $x not set
-
-
-
assertIsA($x, $t)
Fail if $x is not the class or type $t
-
-
-
assertNotA($x, $t)
Fail if $x is of the class or type $t
-
-
-
assertEqual($x, $y)
Fail if $x == $y is false
-
-
-
assertNotEqual($x, $y)
Fail if $x == $y is true
-
-
-
assertIdentical($x, $y)
Fail if $x == $y is false or a type mismatch
-
-
-
assertNotIdentical($x, $y)
Fail if $x == $y is true and types match
-
-
-
assertReference($x, $y)
Fail unless $x and $y are the same variable
-
-
-
assertCopy($x, $y)
Fail if $x and $y are the same variable
-
-
-
assertWantedPattern($p, $x)
Fail unless the regex $p matches $x
-
-
-
assertNoUnwantedPattern($p, $x)
Fail if the regex $p matches $x
-
-
-
assertNoErrors()
Fail if any PHP error occoured
-
-
-
assertError($x)
Fail if no PHP error or incorrect message
-
-
-
assertErrorPattern($p)
Fail unless the error matches the regex $p
-
-
-
- All assertion methods can take an optional description to
- label the displayed result with.
- If omitted a default message is sent instead which is usually
- sufficient.
- This default message can still be embedded in your own message
- if you include "%s" within the string.
- All the assertions return true on a pass or false on failure.
-
-
- Some examples...
-
-$variable = null;
-$this->assertNull($variable, 'Should be cleared');
-
- ...will pass and normally show no message.
- If you have
- set up the tester to display passes
- as well then the message will be displayed as is.
-
-$this->assertIdentical(0, false, 'Zero is not false [%s]');
-
- This will fail as it performs a type
- check as well as a comparison between the two values.
- The "%s" part is replaced by the default
- error message that would have been shown if we had not
- supplied our own.
- This also allows us to nest test messages.
-
- This one takes some explanation as in fact they all pass!
-
-
- PHP errors in SimpleTest are trapped and placed in a queue.
- Here the first error check catches the "Disaster"
- message without checking the text and passes.
- This removes the error from the queue.
- The next error check tests not only the existence of the error,
- but also the text which here matches so another pass.
- With the queue now empty the last test will pass as well.
- If any unchecked errors are left at the end of a test method then
- an exception will be reported in the test.
- Note that SimpleTest cannot catch compile time PHP errors.
-
-
- The test cases also have some convenience methods for debugging
- code or extending the suite...
-
-
-
-
setUp()
Runs this before each test method
-
-
-
tearDown()
Runs this after each test method
-
-
-
pass()
Sends a test pass
-
-
-
fail()
Sends a test failure
-
-
-
error()
Sends an exception event
-
-
-
sendMessage()
Sends a status message to those displays that support it
-
-
-
signal($type, $payload)
Sends a user defined message to the test reporter
-
-
-
dump($var)
Does a formatted print_r() for quick and dirty debugging
- If you want a test case that does not have all of the
- UnitTestCase assertions,
- only your own and assertTrue(),
- you need to extend the SimpleTestCase
- class instead.
- It is found in simple_test.php rather than
- unit_tester.php.
- See later if you
- want to incorporate other unit tester's
- test cases in your test suites.
-
- You won't often run single test cases except when bashing
- away at a module that is having difficulty and you don't
- want to upset the main test suite.
- Here is the scaffolding needed to run the a lone test case...
-
- 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.
-
-
- 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.
-
-
- SimpleTest includes a special form of test case for the testing
- of web page actions.
- The WebTestCase includes facilities
- for navigation, content and cookie checks and form handling.
- Usage of these test cases is similar to the
- UnitTestCase...
-
-class TestOfLastcraft extends WebTestCase {
-}
-
- Here we are about to test the
- Last Craft site itself.
- If this test case is in a file called lastcraft_test.php
- then it can be loaded in a runner script just like unit tests...
-
- The get() 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 get() method will still return true.
-
-
- Assuming that the web server for the Last Craft site is up
- (sadly not always the case), we should see...
-
- To confirm that the page we think we are on is actually the
- page we are on, we need to verify the page content.
-
-class TestOfLastcraft extends WebTestCase {
-
- function testHomepage() {
- $this->get('http://www.lastcraft.com/');
- $this->assertWantedPattern('/why the last craft/i');
- }
-}
-
- 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.
-
-
- Here is the list of possible content assertions...
-
-
-
-
assertTitle($title)
Pass if title is an exact match
-
-
-
assertWantedPattern($pattern)
A Perl pattern match against the page content
-
-
-
assertNoUnwantedPattern($pattern)
A Perl pattern match to not find content
-
-
-
assertWantedText($text)
Pass if matches visible and "alt" text
-
-
-
assertNoUnwantedText($text)
Pass if doesn't match visible and "alt" text
-
-
-
assertLink($label)
Pass if a link with this text is present
-
-
-
assertNoLink($label)
Pass if no link with this text is present
-
-
-
assertLinkById($id)
Pass if a link with this id attribute is present
-
-
-
assertNoLinkById($id)
Pass if no link with this id attribute is present
-
-
-
assertField($name, $value)
Pass if an input tag with this name has this value
-
-
-
assertFieldById($id, $value)
Pass if an input tag with this id has this value
-
-
-
assertResponse($codes)
Pass if HTTP response matches this list
-
-
-
assertMime($types)
Pass if MIME type is in this list
-
-
-
assertAuthentication($protocol)
Pass if the current challenge is this protocol
-
-
-
assertNoAuthentication()
Pass if there is no current challenge
-
-
-
assertRealm($name)
Pass if the current challenge realm matches
-
-
-
assertHeader($header, $content)
Pass if a header was fetched matching this value
-
-
-
assertNoUnwantedHeader($header)
Pass if a header was not fetched
-
-
-
assertHeaderPattern($header, $pattern)
Pass if a header was fetched matching this Perl regex
-
-
-
assertCookie($name, $value)
Pass if there is currently a matching cookie
-
-
-
assertNoCookie($name)
Pass if there is currently no cookie of this name
-
-
-
- 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.
-
-
- So now we could instead test against the title tag with...
-
-$this->assertTitle('The Last Craft? Web developer tutorials on PHP, Extreme programming and Object Oriented development');
-
- As well as the simple HTML content checks we can check
- that the MIME type is in a list of allowed types with...
-
- 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...
-
- 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 WebTestCase 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...
-
- 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...
-
- If the target is a button rather than an anchor tag, then
- clickSubmit() should be used
- with the button title...
-
-$this->clickSubmit('Go!');
-
-
-
- The list of navigation methods is...
-
-
-
-
getUrl()
The current location
-
-
-
get($url, $parameters)
Send a GET request with these parameters
-
-
-
post($url, $parameters)
Send a POST request with these parameters
-
-
-
head($url, $parameters)
Send a HEAD request without replacing the page content
-
-
-
retry()
Reload the last request
-
-
-
back()
Like the browser back button
-
-
-
forward()
Like the browser forward button
-
-
-
authenticate($name, $password)
Retry after a challenge
-
-
-
restart()
Restarts the browser as if a new session
-
-
-
getCookie($name)
Gets the cookie value for the current context
-
-
-
ageCookies($interval)
Ages current cookies prior to a restart
-
-
-
clearFrameFocus()
Go back to treating all frames as one page
-
-
-
clickSubmit($label)
Click the first button with this label
-
-
-
clickSubmitByName($name)
Click the button with this name attribute
-
-
-
clickSubmitById($id)
Click the button with this ID attribute
-
-
-
clickImage($label, $x, $y)
Click an input tag of type image by title or alt text
-
-
-
clickImageByName($name, $x, $y)
Click an input tag of type image by name
-
-
-
clickImageById($id, $x, $y)
Click an input tag of type image by ID attribute
-
-
-
submitFormById($id)
Submit a form without the submit value
-
-
-
clickLink($label, $index)
Click an anchor by the visible label text
-
-
-
clickLinkById($id)
Click an anchor by the ID attribute
-
-
-
getFrameFocus()
The name of the currently selected frame
-
-
-
setFrameFocusByIndex($choice)
Focus on a frame counting from 1
-
-
-
setFrameFocus($name)
Focus on a frame by name
-
-
-
-
-
- The parameters in the get(), post() or
- head() 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.
-
-
- The retry(), back() and
- forward() 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.
-
-
- 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.
-
-
- 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
- tools for testing web forms
- as well.
-
- 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...
-
-
-
-
getTransportError()
The last socket error
-
-
-
showRequest()
Dump the outgoing request
-
-
-
showHeaders()
Dump the incoming headers
-
-
-
showSource()
Dump the raw HTML page content
-
-
-
ignoreFrames()
Do not load framesets
-
-
-
setCookie($name, $value)
Set a cookie from now on
-
-
-
addHeader($header)
Always add this header to the request
-
-
-
setMaximumRedirects($max)
Stop after this many redirects
-
-
-
setConnectionTimeout($timeout)
Kill the connection after this time between bytes
-
-
-
useProxy($proxy, $name, $password)
Make requests via this proxy URL
-
-
-
- These methods are principally for debugging.
-
-
-
-
- Copyright Marcus Baker, Jason Sweat, Perrick Penet 2004
-