Form testing documentation

This page...

Submitting a simple form

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...

<form>
    <input type="text" name="a" value="A default" />
    <input type="submit" value="Go" />
</form>
Which looks like this...

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". As usual, we could use a pattern expectation instead of a fixed string.
class SimpleFormTests extends WebTestCase {
    function testDefaultValue() {
        $this->get('http://www.lastcraft.com/form_testing_documentation.php');
        $this->assertField('a', new PatternExpectation('/default/'));
    }
}
We could submit the form straight away, but first we'll change the value of the text field and only then submit it...
class SimpleFormTests extends WebTestCase {
    function testDefaultValue() {
        $this->get('http://www.my-site.com/');
        $this->assertField('a', 'A default');
        $this->setField('a', 'New value');
        $this->click('Go');
    }
}
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. 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 HTMLTidy should be used, or any of the HTML and CSS validators already out there.

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>
Which looks like...

Select type of user to add:

The following test will confirm it...

class SimpleFormTests extends WebTestCase {
    ...
    function testNoSuperuserChoiceAvailable() {
        $this->get('http://www.lastcraft.com/form_testing_documentation.php');
        $this->assertFalse($this->setField('type', 'Superuser'));
    }
}
The current selection will not be changed if the new value is not an option.

Here is the full list of widgets currently supported...

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.

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.

Fields with multiple values

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...

<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>
Which renders as...

Create privileges allowed:
Retrieve privileges allowed:
Update privileges allowed:
Destroy privileges allowed:

If we wish to disable all but the retrieval privileges and submit this information we can do it like this...

class SimpleFormTests extends WebTestCase {
    ...
    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');
    }
}
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.

Forms which use javascript to set a hidden field

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 not work:

class SimpleFormTests extends WebTestCase {
    function testEmulateMyJavascriptForm() {
        // This does *not* work
        $this->setField('a_hidden_field', '123');
        $this->clickSubmit('OK');
    }
}
Instead, you need to pass the additional form parameters to the clickSubmit() method:
class SimpleFormTests extends WebTestCase {
    function testMyJavascriptForm() {
        $this->clickSubmit('OK', array('a_hidden_field'=>'123'));
    }

}
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 Selenium to ensure a complete test.

Raw posting

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.

class SimpleFormTests extends WebTestCase {
    ...    
    function testAttemptedHack() {
        $this->post(
                'http://www.my-site.com/add_user.php',
                array('type' => 'superuser'));
        $this->assertNoText('user created');
    }
}
By adding data to the WebTestCase::post() 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.

References and related information...