zz log

zaininnari Blog

CakePHP Ajax のテスト(CakeTestCase)

CakePHPAjax のテスト(CakeTestCase)を作ります。

前提

WebTestCase

WebTestCase では、fixture (テスト用のデータを準備する機能)が使えないので、
テストが面倒になるので、今回は使っていません。


WebTestCase で fixture 方法は、以下が参考になります。

コントローラー

<?php
class StatesController extends AppController {
  function ajax_edit($id = null) {
// json で返すので、エラーを抑止
    Configure::write('debug', 0);
// json 用のレイアウトをセット
// echo $content_for_layout; だけが記述されています。
    $this->layout = 'ajax';
// Ajax のアクセスかどうか判定
// RequestHandler コンポーネントを使用
    if (!$id || !$this->RequestHandler->isAjax()) {
      return $this->set('state', array('error' => true, 'message' => __('Invalid id or not XMLHttpRequest.', true)));
    }
// data[<Model>][<param>] の形式でなければ、 $this->data の代わりに
// $this->params['form'] で POST 内容を受け取れる
    if (empty($this->params['form'])) {
      return $this->set('state', array('error' => true, 'message' => __('The State could not be saved. Please, try again.', true)));
    }
    $data['State'] = $this->params['form'];
    $data['State']['id'] = $id;
    if (isset($data['State']['hex']) && strpos($data['State']['hex'], '#') === 0) {
      $data['State']['hex'] = substr($data['State']['hex'], 1);
    }
    if ($this->State->save($data, true, array('name', 'hex', 'type'))) {
      $this->set('state', array('error' => false, 'message' => __('The State has been saved', true)));
    } else {
      $this->set('state', array('error' => true, 'message' => __('The State could not be saved. Please, try again.', true)));
    }
  }
}

ビュー

[app/views/states/ajax_edit.ctp]

<?php
echo json_encode($state);
?>

テストケース

<?php
class StatesControllerTestCase extends WhiskCakeTestCase {
  function testAjaxEdit() {
    $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
    $stateId = 1;

    $expect = $_POST = array(
      'name' => 'changeName',
      'hex' => 'ffffff',
      'type' => '1',
    );
    $this->_initControllerAction('ajax_edit', 'p/whisk/states/ajax_edit/' . $stateId, true);
    $this->States->ajax_edit($stateId);
    $this->assertFalse($this->States->redirectUrl);
    $this->assertFalse($this->States->statusCode);

    $output = $this->States->render('ajax_edit');
    $this->assertFalse(json_decode($output)->error);
    $state = $this->States->State->findById($stateId);

    $this->assertEqual(array_intersect_key($state['State'], $expect), $expect);
  }

解説

<?php
    $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';

ajax のリクエストかどうかを判定する「$this->RequestHandler->isAjax()」を true にします。
endTest で、 $_SERVER['HTTP_X_REQUESTED_WITH'] を unset しないと他のテストに影響します。

[cake/libs/controller/components/request_handler.php]

<?php
class RequestHandlerComponent extends Object {

/**
 * Returns true if the current HTTP request is Ajax, false otherwise
 *
 * @return boolean True if call is Ajax
 * @access public
 */
  function isAjax() {
// $_SERVER['HTTP_X_REQUESTED_WITH'] === "XMLHttpRequest" と同じです。
// env 関数は、basic.php で定義されています。
    return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
  }
}
<?php
    $expect = $_POST = array(
      'name' => 'changeName',
      'hex' => 'ffffff',
      'type' => '1',
    );

XMLHttpRequest の送信するデータ形式が、
{name : 'name', hex : 'FF0033', type : '0'}
なら、

<?php
$this->param['form'] = array(
  'name' => 'name',
  'hex' => 'FF0033',
  'type' => '0'
);

と格納されます。
$this->param['form'] には、 $_POST の値が格納されます。


また、
$this->data[] で受け取りたい場合は、
{data : {State: {name : 'name', hex : 'FF0033', type : '0'}}}
とすることで、

<?php
$this->param['form'] = array(
  'data' => array(
    'State' => array(
      'name' => 'name',
      'hex' => 'FF0033',
      'type' => '0'
    )
  )
);

と格納されます。
特に、
$this->param['form']['data'] にあるものは、
$this->data に格納され、Cake 風のデータ形式になります。

<?php
$this->data = array(
  'State' => array(
    'name' => 'name',
    'hex' => 'FF0033',
    'type' => '0'
  )
);

最後に、
この例では、$_POST に値をセットしているので、endTest で、 $_POST を unset しないと他のテストに影響します。