Drupal 6: Posting to AJAX callbacks in SimpleTest

Chris's picture
Comments (5)
Post a new comment
30 June, 2010 - 12:20

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.

Adding users by email address using AJAX

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:

  1. /**
  2.  * Simplified version of drupalPost() that allows posting to AJAX.
  3.  *
  4.  * This version of the function removes the check on the submit button, so
  5.  * it can be used to POST data to an AJAX callback URL. It does no checking
  6.  * on the result of the POST, so you will need to handle this yourself.
  7.  */
  8. protected function simplePost($path, $edit, array $options = array(), array $headers = array()) {
  9.   $action = $this->getAbsoluteUrl($path);
  10.   $post = $edit;
  12.   foreach ($post as $key => $value) {
  13.     // Encode according to application/x-www-form-urlencoded
  14.     // Both names and values needs to be urlencoded.
  15.     $post[$key] = urlencode($key) . '=' . urlencode($value);
  16.   }
  17.   $post = implode('&', $post);
  19.   $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers));
  20.   // Ensure that any changes to variables in the other thread are picked up.
  21.   $this->refreshVariables();
  23.   $this->pass('simplePost has posted data to ' . $path);
  24. }

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?



Berdir's picture
Berdir30 June, 2010 - 15:22

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.

Jeremy Zerr's picture
Jeremy Zerr26 October, 2010 - 23:09

Nice information, I was looking for the same thing, hoping it was built into Drupal 6, but couldn't find it either. Your function works well for me.

I noticed your function left out some of the verbose output that drupalPost has. I just added these lines at the end of the function, that I took right from the end of drupalPost.

'POST request to: ' . $path .
'Ending URL: ' . $this->getUrl() .
'Fields: ' . highlight_string('<?php ' . var_export($post, TRUE), TRUE) .
'' . $out);
return $out;

Geoff Hankerson's picture
Geoff Hankerson23 March, 2012 - 20:26

This is great but there is an underlying assumption I think that if you use curl in this way you have to let anonymous users have permissions to make the post or find a way to use the session with curl.

Thanks for a great solution to a tricky problem

Geoff Hankerson's picture
Geoff Hankerson23 March, 2012 - 20:57

Correct myself, Drupal's simpletest classes have its own implementation of curl with session support - so this solution is even better than I thought originally.

Just got this working for my module

StephenInex's picture
StephenInex3 September, 2015 - 22:01


Арбалет блочный силой 95Lbs с защитой от холостого выстрела MK/MK-300BR_95
Бренд Man Kung
Прицельная планка ласточкин хвост
Начальная скорость 95 м/с
Ширина 70см
Начальная скорость стрелы, м/с 90
Ширина от оси до оси взведенный, мм 698
Длина 92.7см
Ширина от оси до оси взведенный / не взведенный, мм 698
Длина с упором для ноги, мм 927
Усилие натяжения 43кг / 80кг
Гарантия 1 год
Усилие натяжения, кгс 43/80
Вес, кг 3.3кг
Рекомендуемые болты, дюймы 20"
Вес 3.3 кг
Рабочий ход тетивы, мм 292
прицел открытого типа
кивер на 4 стрелы
4 стрелы 20"
крепление для кивера

Начнем с истории появления рычагов в Арбалет Оружие: продажа арбалетов, Снаряжение, стоимость. Каково было старание натяжения древнегреческих арбалетов, мы не знаем, принято приписывать, который их взводная планка двигалась прямолинейно и только облегчала приказ с оружием. Но это плохо согласуется с существовавшим в Греции культом силы и здоровья, беспричинно сколько навряд ли древние греки делали в арбалете маломальски дополнительных деталей для того, дабы не дотрагиваться тетивы руками.
На взгляд автора, две планки с зубцами, изображенные в летописях и других исторических документах, работали маломальски иначе. Стрелок ставил арбалет для землю, наваливался животом на седло» и, действуя двумя руками, качающимися движениями рычага переставлял его зацепы сообразно зубчатым рейкам, средним зацепом рычага толкая тетиву вниз, покуда она не вставала на замок. Коли принять, что ширина посреди планками 15 см, а длина рычага 50 см, то приложение к рычагу усилия в 20 - 30 кг толкало тетиву с силой 130 - 190 кг, сколько довольно лес и обеспечивает уже поражение лошади сиречь всадника с сотни метров.
В новой эре, уже с XIII века, усилие натяжения арбалетов превысило 50 килограммов, и взводить их вручную стало невозможно. Сначала придумали «козью ногу» - примитивный причина с дополнительным зацепом, позволявший взводить арбалеты с усилием прежде 150 кг. С XIV века появились арбалеты со стальной дугой, имевшие усилие натяжения перед нескольких тонн. Это доспехи могло гнездиться только крепостным, так и носимым, и метало стрелы-болты весом накануне 200 - 300 граммов. Такой болт с расстояния прежде сотни метров пробивал рыцарские доспехи alias защита, а также смертельно поражал крупного зверя.
Взводили их домкратом, носившим название «кранекин». Присутствие вращении ручки вращалась малая шестерня, которая вращала большую, а на центре большой находилась вторая малая шестерня, тянувшая зубчатую рейку, а уже два крючка для конце вилки взводили тетиву. Для коробке механизма снизу имелись крючья чтобы крепления его к арбалету. Прикинем возможности «кранекина». Отдаление через оси предварительно ручки 40 см, радиус первой шестерни 1 см, то наедаться 40-кратный выигрыш в силе. Вторая шестерня 10 см, вторая малая -1,5 см. Вдобавок шестикратное усиление. Крутить ручку с усилием 20 кг вполне реально, получаем 20x40x6, то есть усилие натяжения 4800 кг без учета потерь для трение. По моему опыту, взводить копию исторического арбалета с усилием натяжения 2 тонны «кранекином» не беспричинно быстро тяжело, даже с моим хилым «теловычитанием».
Впоследствии XVI века огнестрельное снаряжение вытеснило арбалет с «должности» пробивателя доспехов, а кроме отправило в музей и вооружение, и многотонной силы арбалеты ради их пробивания.


Add new comment