In Drupal 6's excellent SimpleTest module, a method called drupalPost() allows you to simulate a button press on a form by taking the form's data and using HTTP POST to submit it. But what if you want to POST data to an AJAX callback URL? By default, SimpleTest checks which submit button you have pressed, but of course, when POSTing using AJAX, you probably won't have pressed a button!
Suppose you have a table of email addresses, as illustrated below, and you want your users to be able to remove them. It would be quite inconvenient to make the user check a box, press 'delete' and then wait for the page to refresh. Instead, you can provide an AJAX callback URL that will allow the email address to be removed when the 'remove' button is pressed, without needing to reload the page, making it much easier for your users.

This generally works by having a URL, say /mymodule/delete-email, and having your JavaScript (jQuery in Drupal's case) take care of making a POST request to that URL, the POSTVARS containing perhaps the email address that is to be removed.
Now suppose this is all set up, but we want to write some tests for this functionality. There is no actual form on the page, so SimpleTest quickly runs into issues. We want to use SimpleTest's drupalPost() to make the POST request, but, as mentioned in the documentation, we need to pass a 'submit' parameter that tells SimpleTest to make sure that the appropriate submit form element is on the page, and make sure it has been pressed.
There is no way of having SimpleTest 'ignore' this submit parameter. It absolutely must be provided, and the test will fail unless that particular submit button is found on the form page.
The solution
I dug around for solutions to this problem, and asked in the Drupal IRC channels for help, but nothing was forthcoming, so I decided that the best way forward was to write my own function. In my module, I have written a 'base' class that inherits directly from DrupalWebTestCase, and then all my test classes inherit from this base.
I simply added this method to my base class, thereby making it available to all test classes:
/**
* Simplified version of drupalPost() that allows posting to AJAX.
*
* This version of the function removes the check on the submit button, so
* it can be used to POST data to an AJAX callback URL. It does no checking
* on the result of the POST, so you will need to handle this yourself.
*/
$action = $this->getAbsoluteUrl($path);
$post = $edit;
foreach ($post as $key => $value) {
// Encode according to application/x-www-form-urlencoded
// Both names and values needs to be urlencoded.
}
$out =
$this-
>curlExec(array(CURLOPT_URL =
> $action, CURLOPT_POST =
> TRUE, CURLOPT_POSTFIELDS =
> $post, CURLOPT_HTTPHEADER =
> $headers));
// Ensure that any changes to variables in the other thread are picked up.
$this->refreshVariables();
$this->pass('simplePost has posted data to ' . $path);
}
This works very similarly to the way drupalPost() works, except that it does not require the 'submit' parameter to be supplied. Of course, it's a very stripped-down version, without the possibility of uploading files and without some of the more stringent checking done by drupalPost(), but it does work, and allows testing of AJAX callback URLs inside your Drupal tests.
Without this method, I would have resorted to testing only the actual functions in my module, more akin to a unit test, which would not check that the menu callbacks were correctly assigned and that all the access permissions were set up correctly. It's clearly not the ideal way to go about testing AJAX callbacks, so what method have you been using, and how would you improve on this?
Simpletest in Drupal 7 (features are usually backported, so it might be in D6 already or at least soon) has a method called drupalPostAJAX(), haven't used it myself though, but there are AJAX tests in D7.