zz log

zaininnari Blog

CakePHP1.3 コントローラーのテスト(testAction なし バージョン)

コントローラーのテストを testAction メソッドを使わず書いた場合の例です。

前提 & 環境

  • テストを実行するのは、コンソール「cake testsuite app all」です。
  • 作業環境
  • 意識する対象
    • PHP5.2 以上の動作を意識します。
      • PHP5.2 でのチェックはしていませんが、PHP5.3 の機能は使用していません。
      • PHP5 の機能(アクセス権など)は、使用します。
    • CakePHP 1.3 以上の動作を意識します。
      • 安定化・高速化が図られたバージョンを選択しています。

素材

<?php
class UsersController extends AppController {

  var $name = 'Users';

  function beforeFilter()
  {
    $this->Auth->allow('add'); // not auth action

    $action = Set::extract($this->params, 'action');
    // no hash passowrd action
    if (in_array($action, array('add', 'edit'), true)) {
      $this->Auth->authenticate = ClassRegistry::init('User');
    }
  }

  function login()
  {
    // Processing is handovered to Auth component.
  }


  function logout()
  {
    $this->Session->setFlash('logout');
    $this->Auth->logout();
    $this->redirect(array('action' => 'index'));
  }

  function index()
  {
    $this->User->recursive = 0;
    $this->set('users', $this->paginate());
  }

  function view($id = null)
  {
    if (!$id) {
      $this->Session->setFlash(__('Invalid User', true));
      $this->redirect(array('action' => 'index'));
    }
    $this->set('user', $this->User->read(null, $id));
  }

  function add()
  {
    if (!empty($this->data)) {
      $this->User->create($this->data);
      if ($this->User->validates()) {
          $path = $this->Auth->getModel()->alias . '.' . $this->Auth->fields['password'];
          $this->data = Set::insert($this->data, $path, $this->Auth->password(Set::extract($this->data, $path)));
        if ($this->User->save($this->data, false)) {
          $this->Session->setFlash(__('The User has been saved', true));
          $this->redirect(array('action' => 'index'));
        }
      }
      $this->Session->setFlash(__('The User could not be saved. Please, try again.', true));
    }
  }

  function edit($id = null) 
  {
    if ((!$id && empty($this->data)) || !$id) {
      $this->Session->setFlash(__('Invalid User', true));
      $this->redirect(array('action' => 'index'));
    }
    if (!empty($this->data)) {
      $this->User->create($this->data);
      if ($this->User->validates()) {
        $path = $this->Auth->getModel()->alias . '.' . $this->Auth->fields['password'];
        $this->data = Set::insert($this->data, $path, $this->Auth->password(Set::extract($this->data, $path)));
        if ($this->User->save($this->data, false)) {
          $this->Session->setFlash(__('The User has been saved', true));
          $this->redirect(array('action' => 'index'));
        }
      }
      $this->Session->setFlash(__('The User could not be saved. Please, try again.', true));
    }
    if (empty($this->data)) {
      $this->data = $this->User->read(null, $id);
    }
  }

  function delete($id = null) 
  {
    if (!$id) {
      $this->Session->setFlash(__('Invalid id for User', true));
      $this->redirect(array('action' => 'index'));
    }
    if ($this->User->delete($id)) {
      $this->Session->setFlash(__('User deleted', true));
      $this->redirect(array('action' => 'index'));
    }
    $this->Session->setFlash(__('The User could not be deleted. Please, try again.', true));
    $this->redirect(array('action' => 'index'));
  }

}

パスワードは、「aaaa」です。

<?php

class UserFixture extends CakeTestFixture {
  var $name = 'User';

  var $fields = array(
    'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
    'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
    'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL),
    'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 200),
    'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 200),
    'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
    'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_unicode_ci', 'engine' => 'InnoDB')
  );

  var $records = array(
    array(
      'id' => 1,
      'created' => '2010-01-01 00:00:00',
      'modified' => '2010-01-01 00:00:00',
      'username' => 'aaaa',
      'password' => '886cda4198f50ec0167c8eb38edf7036c838c3aa'
    ),
  );

}
<?php

App::import(null, 'CakeTestCase', false, CAKE_TESTS_LIB);

class WhiskCakeTestCase extends CakeTestCase
{
// 対象のコントローラー名
  /**
   * @var AppController
   */
  public $name;
// 対象のコントローラーインスタンス
  protected $_this;
// startup メソッドで作成するコントローラーインスタンスを作成します。
  protected function _createControllerInstance(&$_this)
  {
    $this->_this = $_this;
    $this->name = $name = str_replace('ControllerTestCase', '', get_class($_this));
    $controllerName = 'Test' . $name . 'Controller';
// コントローラーインスタンスを作成します。
    $_this->$name =& new $controllerName;
// モデルを読み込みとコンポーネントの読み込みを行います。
    $_this->$name->constructClasses();
// params プロパティは、本来 Dispatcher クラスで設定されるため、手動で設定します。
// また、
// Auth コンポーネント でも必要になります。
    $_this->$name->params['controller'] = strtolower($name);
    $_this->$name->params['pass'] = array();
    $_this->$name->params['named'] = array();
  }

// 各アクションで必要な初期値をセットします。
  protected function _initControllerAction($action = 'index', $url = '/', $login = false)
  {
    $this->_this->{$this->name}->params['action'] = $action;
    $this->_this->{$this->name}->params['url']['url'] = $url;
// $_SESSION['Auth']['User'] に値があれば、認証していることになります。
// Auth->user() は、$_SESSION['Auth']['User'] をチェックします。
    if ($login) {
      $this->_this->{$this->name}->Session->write('Auth.User', array(
        'id' => 1,
        'username' => 'admin',
      ));
    }
// ・コンポーネントの初期化(Component::initialize)、
// ・controller::beforeFilter の実行、
// ・コンポーネントの実行($this->Component->triggerCallback('startup', $this))
// を行います。
    $this->_this->{$this->name}->startupProcess();
  }

  function endTest()
  {
// ログアウト
    $this->_this->{$this->name}->Auth->logout();
// セッションを空にする
    unset($_SESSION);
// コントローラーインスタンスを削除
    unset($this->_this->{$this->name});
// 登録したクラスをすべて削除する
    ClassRegistry::flush();
  }
}


bake で作成したものに手を加えて作成しました。

<?php

App::import('Controller', 'Users');

// コントローラークラスを継承したテスト用のコントローラークラスを作成します。
// Render の抑制や、実際にリダイレクトをしては困るので、
// 処理を書き換えています。
class TestUsersController extends UsersController
{
  public $autoRender = false;
  public $redirectUrl = false;
  public function redirect($url, $status = null, $exit = true)
  {
    return $this->redirectUrl = $url;
  }
}

class UsersControllerTestCase extends WhiskCakeTestCase
{
// このコメント部分はなくとも大丈夫です。
// Eclipse の補完用です。
  /**
   * controller
   *
   * @var TestUsersController
   */
  var $Users;
  var $fixtures = array('app.user');

// test*** のメソッドを実行する前に呼ばれます。
// コントローラーのインスタンスを作成します。
  function startTest() {
    $this->_createControllerInstance($this);
  }

// なくてもOK
  function endTest() {
    parent::endTest();
  }

  function testNologin()
  {
// ログインが不要なのは、add アクションのみ

// コントローラーアクションの初期化
    $this->_initControllerAction('index', 'users', false);
// Auth コンポーネントでリダイレクトがあれば、
// $this->Users->redirectUrl に 「/users/login」がセットされるはず
    $this->assertEqual($this->Users->redirectUrl, '/users/login');

// $this->Users->redirectUrl を初期化
    $this->Users->redirectUrl = false;
    $this->_initControllerAction('add', 'users/add', false);
// add アクションはログイン不要のため、「false」を期待
    $this->assertFalse($this->Users->redirectUrl);

    $this->Users->redirectUrl = false;
    $this->_initControllerAction('edit', 'users/edit/1', false);
    $this->assertEqual($this->Users->redirectUrl, '/users/login');

    $this->Users->redirectUrl = false;
    $this->_initControllerAction('view', 'users/view/1', false);
    $this->assertEqual($this->Users->redirectUrl, '/users/login');

    $this->Users->redirectUrl = false;
    $this->_initControllerAction('delete', 'users/delete/1', false);
    $this->assertEqual($this->Users->redirectUrl, '/users/login');
  }

  function testLogin() {
    $this->Users->data = array('User' => array(
      'username' => 'aaaa',
      'password' => 'aaaa'
    ));
    $this->_initControllerAction('login', 'users/login', false);
    $this->assertEqual($this->Users->redirectUrl, '/');
    $this->assertNotEqual($this->Users->Auth->user(), null);
  }

  function testLogout() {
    $this->_initControllerAction('logout', 'users/logout', true);
    $this->assertNotEqual($this->Users->Auth->user(), null);
    $this->Users->logout();
    $this->assertNull($this->Users->Auth->user());
    $this->assertEqual($this->Users->redirectUrl, array('action' => 'index'));
  }

  function testIndex() {
// 第三引数 true : $this->Users->Auth->user() を成功させる
    $this->_initControllerAction('index', 'users', true);
    $this->assertFalse($this->Users->redirectUrl);
    $this->assertNotEqual($this->Users->Auth->user(), null);
    $this->Users->index();
    $output = $this->Users->render('index');
    $this->assertFalse(strpos($output, '<pre class="cake-debug">'));
  }


  function testAdd() {
    $this->_initControllerAction('add', 'users/add', true);

    $beforeCount = $this->Users->User->find('count');
    $this->assertEqual(1, $beforeCount);

    $this->Users->data = array('User' => array(
      'username' => 'bbbb',
      'password' => 'cccc'
    ));
    $this->Users->add();
    $this->assertEqual($this->Users->redirectUrl, array('action' => 'index'));

    $afterCount = $this->Users->User->find('count');
    $this->assertEqual($beforeCount + 1, $afterCount);

    $user = $this->Users->User->findById($afterCount);
    $expect = array(
      'username' => $this->Users->data['User']['username'],
      'password' => $this->Users->Auth->hashPasswords($this->Users->data['User']['password']),
    );
    $this->assertEqual(array_intersect_key($user['User'], $expect), $expect);
  }

  function testDelete() {
    $this->_initControllerAction('delete', 'users/delete/1', true);
    $beforeCount = $this->Users->User->find('count');
    $this->Users->delete(1);
    $this->assertEqual($this->Users->redirectUrl, array('action' => 'index'));

    $afterCount = $this->Users->User->find('count');
    $this->assertEqual($beforeCount - 1, $afterCount);
    $this->assertEqual(false, $this->Users->User->findById(1));
  }

  function testEdit() {
    $this->_initControllerAction('edit', 'users/edit/1', true);
    $this->Users->data = array('User' => array(
      'id' => '1',
      'username' => 'modified',
      'password' => 'modified'
    ));
    $this->Users->edit(1);
    $this->assertEqual($this->Users->redirectUrl, array('action' => 'index'));

    $user = $this->Users->User->findById(1);
    $expect = array(
      'username' => $this->Users->data['User']['username'],
      'password' => $this->Users->Auth->hashPasswords($this->Users->data['User']['password']),
    );
    $this->assertEqual(array_intersect_key($user['User'], $expect), $expect);
  }


}