Monthly Archives: 7月 2010 - Page 2

CakePHP 1.3で単体テスト

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHP 1.3で単体テスト
Share on Facebook

やり方

bake test でテストケース&フィクスチャを作成する場合

cake bake test
cake bake fixture 

app/tests/ 以下に自分でテストケースを書く場合

  1. テスト用データベース定義を app/config/database.php に書く
  2. bakeコマンドでテストケースとフィクスチャの雛形を作成する
  3. 実行する
# コントローラーのusos_controllerをテストする場合
cake testsuite app case controllers/usos_controller 

# コンポーネントのusos_commonをテストする場合
cake testsuite app case components/usos_common

redirectやexitを使うメソッドをテストする時の注意

テスト中にredirectメソッドやexitsメソッドを踏むと、ただちにテストが終了してしまいます。下記URLを参考にして、独自のtestsuite.phpとcontroller.phpを用意する方法でうまくいきました。記事ではCakePHP 1.2.7を使っているようですが、CakePHP 1.3.2でもうまく動きました。

テスト時のredirect()やcakeError() の結果を拾う。+ DebugKit使用時も。 – ziegler(チーグラー) ver.6.0

公式マニュアル にある方法 (something_controller.test.php と /cake/tests/lib/cake_test_case.php を修正する) ではうまく動きませんでした。結局redirectの瞬間に終了してしまいました。何が間違っているのか。

[cakephp]testActionでredirectするアクションをテストする方法 | Ryuzee.com を参考にPHP拡張のrunkitを使うのも検討しましたが、どう探してもこのライブラリをWindowsで動かす方法が見つかりませんでした。ソースからコンパイルする方法もあるようですが、最新版でもないVisualStudioをインストールしなければならないなど面倒なようだったので、やめました。

それにしても、基本的な単体テストの準備をするのに手間がかかりすぎです。CakePHP 2.0ではPHPUnitが採用されるようなので、早くリリースしていただきたいところ。

参考

CakePHP 単体テストで使うメソッドのコールバックの実行順

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHP 単体テストで使うメソッドのコールバックの実行順
Share on Facebook

公式マニュアルだけではSimpleTestのコールバックの実行される順序がわからなかったので、調べた結果をメモしておきます。

下のようなコードを実行してみました。実際のテストメソッドは「testHige()」です。

<?php
App::import('Controller', 'Usos');

class TestUsosController extends UsosController {
    var $autoRender = false;

    function redirect($url, $status = null, $exit = true) {
        $this->redirectUrl = $url;
    }
}

class UsosControllerTestCase extends CakeTestCase {
    var $fixtures = array('app.uso');

    function startTest() {
        echo("startTest\n");
        $this->Usos =& new TestUsosController();
        $this->Usos->constructClasses();
    }

    function endTest() {
        echo("endTest\n");
        unset($this->Usos);
        ClassRegistry::flush();
    }
    
    function start() {
        echo("start\n");
    }
    
    function end() {
        echo("end\n");
    }
    
    function startCase() {
        echo("startCase\n");
    }
    
    function endCase() {
        echo("endCase\n");
    }
    
    function before($method) {
        echo($method . " before\n");
    }
    
    function after($method) {
        echo($method . " after\n");
    }

    // 実際のテストメソッド
    function testHige() {
        echo("testHige\n");
    }
}
?>

実行結果は以下の通りです。

start before
start
start after
startCase before
startCase
startCase after
testHige before
testHige
testHige after
endCase before
endCase
endCase after
end before
end
end after

大まかに書くと「start() → startCase() → testHige() → endCase() → end()」の順に実施され、それぞれのメソッドの前後にbefore($method) と after($method) が呼び出されるようです。

参考

CakePHP1.3 + XAMPP 1.7.3でコードカバレッジを取得できるようにする

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHP1.3 + XAMPP 1.7.3でコードカバレッジを取得できるようにする
Share on Facebook

CakePHPでは「http://{アプリURL}/test.php」で単体テスト用ページを表示できます。ここに「Analyze Code Coverage」なんてリンクがあるのですが、私の環境ではここをクリックするとApacheが強制終了させられて、コードカバレッジを取得できませんでした。

どうやらXAMPP 1.7.3に含まれているxDebugのバージョンが原因のようでした(v2.0.6 rc)。そこで最新のxDebug 2.1.0 を入れたところ、無事取得できるようになりました。

xDebugの入れ替え方ですが、Xdebug: Support; Tailored Installation Instructionsにしたがってやるのが一番わかりやすいです。ここのフォームにphpinfoの結果をまるごとコピペするだけで、DLするファイルからphp.iniの設定まですべてやり方を出してくれます。

参考

シェルからcakeErrorメソッドを呼び出すと失敗する

このエントリーをはてなブックマークに追加
はてなブックマーク - シェルからcakeErrorメソッドを呼び出すと失敗する
Share on Facebook

CakePHP 1.3.2 & PHP5.3.1にて。

エラー処理は「$this->cakeError(, [array parameters]);」を使うといいらしいので、使ってみました。その結果、Controllerで使う場合は問題ありませんが、Shell上で呼び出すと以下のようなエラーが出ました。

../cake/console/cake uso

Notice: Undefined variable: name in C:\xampp\htdocs\sandbox\cake\console\error.php on line 68

Call Stack:
    0.0898     540608   1. {main}() C:\xampp\htdocs\sandbox\cake\console\cake.php:0
    0.1060     541392   2. ShellDispatcher->ShellDispatcher() C:\xampp\htdocs\sandbox\cake\console\cake.php:660
    0.4175    2728408   3. ShellDispatcher->dispatch() C:\xampp\htdocs\sandbox\cake\console\cake.php:139
    1.2805    9165232   4. usoShell->main() C:\xampp\htdocs\sandbox\cake\console\cake.php:377
    1.2805    9165232   5. usoCommonComponent->uho() C:\xampp\htdocs\sandbox\uso\vendors\shells\uso.php:30
    1.2805    9165736   6. Object->cakeError() C:\xampp\htdocs\sandbox\uso\controllers\components\uso_common.php:10
    1.2807    9165992   7. ErrorHandler->__construct() C:\xampp\htdocs\sandbox\cake\libs\object.php:201
    1.3140    9166968   8. call_user_func() C:\xampp\htdocs\sandbox\cake\console\error.php:56
    1.3140    9166984   9. ErrorHandler->error() C:\xampp\htdocs\sandbox\cake\console\error.php:0


Notice: Undefined variable: code in C:\xampp\htdocs\sandbox\cake\console\error.php on line 68

Call Stack:
    0.0898     540608   1. {main}() C:\xampp\htdocs\sandbox\cake\console\cake.php:0
    0.1060     541392   2. ShellDispatcher->ShellDispatcher() C:\xampp\htdocs\sandbox\cake\console\cake.php:660
    0.4175    2728408   3. ShellDispatcher->dispatch() C:\xampp\htdocs\sandbox\cake\console\cake.php:139
    1.2805    9165232   4. usoShell->main() C:\xampp\htdocs\sandbox\cake\console\cake.php:377
    1.2805    9165232   5. usoCommonComponent->uho() C:\xampp\htdocs\sandbox\uso\vendors\shells\uso.php:30
    1.2805    9165736   6. Object->cakeError() C:\xampp\htdocs\sandbox\uso\controllers\components\uso_common.php:10
    1.2807    9165992   7. ErrorHandler->__construct() C:\xampp\htdocs\sandbox\cake\libs\object.php:201
    1.3140    9166968   8. call_user_func() C:\xampp\htdocs\sandbox\cake\console\error.php:56
    1.3140    9166984   9. ErrorHandler->error() C:\xampp\htdocs\sandbox\cake\console\error.php:0


Notice: Undefined variable: message in C:\xampp\htdocs\sandbox\cake\console\error.php on line 68

Call Stack:
    0.0898     540608   1. {main}() C:\xampp\htdocs\sandbox\cake\console\cake.php:0
    0.1060     541392   2. ShellDispatcher->ShellDispatcher() C:\xampp\htdocs\sandbox\cake\console\cake.php:660
    0.4175    2728408   3. ShellDispatcher->dispatch() C:\xampp\htdocs\sandbox\cake\console\cake.php:139
    1.2805    9165232   4. usoShell->main() C:\xampp\htdocs\sandbox\cake\console\cake.php:377
    1.2805    9165232   5. usoCommonComponent->uho() C:\xampp\htdocs\sandbox\uso\vendors\shells\uso.php:30
    1.2805    9165736   6. Object->cakeError() C:\xampp\htdocs\sandbox\uso\controllers\components\uso_common.php:10
    1.2807    9165992   7. ErrorHandler->__construct() C:\xampp\htdocs\sandbox\cake\libs\object.php:201
    1.3140    9166968   8. call_user_func() C:\xampp\htdocs\sandbox\cake\console\error.php:56
    1.3140    9166984   9. ErrorHandler->error() C:\xampp\htdocs\sandbox\cake\console\error.php:0

Error:

/cake/console/error.php の コンストラクタ内の下記の部分でしくじっているようです。

function __construct($method, $messages) {
    $this->stdout = fopen('php://stdout', 'w');
    $this->stderr = fopen('php://stderr', 'w');
    call_user_func_array(array(&$this, $method), $messages); //NG
}

引数$methodに”error”を、$messagesに配列を入れても、なぜかerrorメソッドに渡る時点で、$messagesの中身が最初の要素の文字列だけになってしまいます。

call_user_func_arrayではなく、call_user_funcメソッドを使うと動くようになりました。

function __construct($method, $messages) {
    $this->stdout = fopen('php://stdout', 'w');
    $this->stderr = fopen('php://stderr', 'w');
    call_user_func(array($this, $method), $messages); // OK
}

この二つのメソッドの根本的な違いは何なのかわかりませんが、とりあえずこれでコンソール・コントローラから両方で正しく使えるようになりました。

検索してもあまり情報が出てきませんでした。詳しい原因を知っている人がいたら教えてください。

CakePHPのシェルに関するトラブルは概して情報量が少なくて困ります。

参考

Qdmailを使ってCakePHP1.3.2のコンソールからメールを送信する

このエントリーをはてなブックマークに追加
はてなブックマーク - Qdmailを使ってCakePHP1.3.2のコンソールからメールを送信する
Share on Facebook

QdmailはCakePHPを初めとしたフレームワークで簡単にメールを作成・送信できるライブラリです。CakePHP1.2 で利用するサンプルは見つかったのですが、1.3で使っている例をあまり見かけず、少しつまづいたのでメモしておきます。

手順

1. Qdmailをダウンロードし、/app/controllers/components に解凍する

2. コンソールプログラムを下記のように作成する

// /app/vendors/shells/uso.php

<?php
class UsoShell extends Shell {
    // 使用するコア
    var $Controller;
    
    // 使用するコンポーネント
    var $Qdmail;
    
    // overrideしてcake起動メッセージを消す
    function startup() {}
    
    // 初期化処理
    function initialize() {
        parent::initialize();
        
        App::import("Core", "Controller");
        App::import("Component", "Qdmail");
        
        $this->Controller =& new Controller();
        $this->Qdmail =& new QdmailComponent(null);
        $this->Qdmail->startup($this->Controller);
    }
    
    // エントリーポイント
    function main() {
        
        // Qdmailの設定
        $this->Qdmail->to('to@uso.uso', '送信先の名前');
        $this->Qdmail->subject('件名');
        $this->Qdmail->from('from@uso.uso' , '送信元の名前');
        $this->Qdmail->text('本文');
        
        // 送信する
        $return = $this->Qdmail->send();
        $this->Qdmail->reset();
        
        if ($return) {
            $this->out("送信完了。");
        } else {
            $this->out("送信失敗...");
        }
    }
}
?>
  1. 実行すれば

補足

何が悪いのかよくわかりませんが、上のコードを実行すると「指定されたパスが見つかりません。」と表示されます。送信するぶんには問題ないのですが、気持ち悪いのでどうにか消せないか方法を探しています。

参考

CakePHPでコンソールアプリを作るときの基本

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHPでコンソールアプリを作るときの基本
Share on Facebook

コンソールアプリケーションの保存先: /app/vendors/shell

基本的な構造

// /app/vendors/shell/uso.php
<?php
class UsoShell extends Shell {
    // モデルを使う場合、配列$usesで指定する
    var $uses = array("Post");

    function main() {
        // ここに命令を記述する
        // ...
        
        // 標準出力 (自動改行する)
        $this->out('hige');
        
        // 標準出力 (自動改行しない)
        $this->out('moja', false);
        
        // 標準エラー出力
        $this->err('bosa');
        
        // 区切り線
        $this->hr();
        
        // 異常終了させる
        $this->error("異常発見", "異常終了します");
    }
}
?>

実行方法

cd /app
../cake/console/cake uso

出力結果

$ ../cake/console/cake uso


Welcome to CakePHP v1.3.2 Console
---------------------------------------------------------------
App : uso
Path: c:\xampp\htdocs\cake\app
---------------------------------------------------------------
hige
mojabosa
---------------------------------------------------------------
Error: 異常発見
異常終了します

参考

Modelのbake中に出る「A displayField could not be automatically detected」とは?

このエントリーをはてなブックマークに追加
はてなブックマーク - Modelのbake中に出る「A displayField could not be automatically detected」とは?
Share on Facebook

Modelをbakeしていると、「A displayField could not be automatically detected would you like to choose one? (y/n)」というメッセージが出ることがあります。

displayFieldというのは、CakePHPが自動で「これは画面に表示するフィールドだろう」と認識してくれるフィールド。具体的には「name」「title」などが該当します。

今bakeしようとしているテーブルにそうした名前のフィールドが見つからない場合、自分で指定しなければならない、ということのようです。

自動で認識されるフィールド名と、それがどう利用されるかの関係はCookbookに書いてありました。

参考

Nabble – CakePHP – Cake Console Baking Model displayField?

CakePHP公式のブログ作成チュートリアルをSQLite3でやってみる

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHP公式のブログ作成チュートリアルをSQLite3でやってみる
Share on Facebook

このチュートリアルの目的

一つのCakePHPコアに複数のアプリケーションを同居させる方法を学べます。DBを作ってdatabase.phpを作成すれば、あとはbakeするだけで基本的な画面が出来上がります。

公式チュートリアルと今回のやり方の違い

公式チュートリアル

  • MySQLでなくSQLite3で作る
  • アプリをCakePHP解凍時のappディレクトリ内に作らず、「{インストールディレクトリ}/blog」内にbakeしたファイルを元に作る
    • 一つのCakePHPコアから複数のアプリを併存させる環境を想定
  • Model/View/Controller はbakeして作る

作成環境

  • CakePHP 1.3.2
  • XAMPP Windows
    用途 場所
    ドキュメントルート c:\xampp\htdocs\
    CakePHPインストールディレクトリ c:\xampp\htdocs\cake
    blogアプリの場所 c:\xampp\htdocs\cake\blog

手順

1. SQLite3を使うための dbo_sqlite3.php を導入する

導入先: {インストールディレクトリ}/cake/libs/model/datasources/dbo/dbo_sqlite3.php

2. コンソールからblogアプリをbakeする

cd c:\xampp\htdocs\cake
mkdir blog
cake/console/cake bake blog

対話式コマンドは↓の通り(Databese Configurationは後で直接いじるので適当でよい)

Welcome to CakePHP v1.3.2 Console
---------------------------------------------------------------
App : sandbox
Path: c:\xampp\htdocs\cake
---------------------------------------------------------------
Bake Project
Skel Directory: C:\xampp\htdocs\cake\cake\console\templates\skel
Will be copied to: c:\xampp\htdocs\cake\blog
---------------------------------------------------------------
Look okay? (y/n/q)
[y] > y
Do you want verbose output? (y/n)
[n] >
---------------------------------------------------------------
Created: blog in c:\xampp\htdocs\cake\blog
---------------------------------------------------------------

Creating file c:\xampp\htdocs\cake\blog\views\pages\home.ctp
Wrote `c:\xampp\htdocs\cake\blog\views\pages\home.ctp`
Welcome page created
Random hash key created for 'Security.salt'
Random seed created for 'Security.cipherSeed'
CAKE_CORE_INCLUDE_PATH set to C:\xampp\htdocs\cake in webroot/index.php
CAKE_CORE_INCLUDE_PATH set to C:\xampp\htdocs\cake in webroot/test.php
Remember to check these value after moving to production server
Your database configuration was not found. Take a moment to create one.
---------------------------------------------------------------
Database Configuration:
---------------------------------------------------------------
Name:
[default] >
Driver: (db2/firebird/mssql/mysql/mysqli/odbc/oracle/postgres/sqlite/sybase)
[mysql] > sqlite3
Driver: (db2/firebird/mssql/mysql/mysqli/odbc/oracle/postgres/sqlite/sybase)
[mysql] > sqlite
Persistent Connection? (y/n)
[n] >
Database Host:
[localhost] >
Port?
[n] >
User:
[root] >
Password:
>
The password you supplied was empty. Use an empty password? (y/n)
[n] > y
Database Name:
[cake] >
Table Prefix?
[n] >
Table encoding?
[n] >

---------------------------------------------------------------
The following database configuration will be created:
---------------------------------------------------------------
Name:         default
Driver:       sqlite
Persistent:   false
Host:         localhost
User:         root
Pass:
Database:     cake
---------------------------------------------------------------
Look okay? (y/n)
[y] >
Do you wish to add another database configuration?
[n] >

Creating file c:\xampp\htdocs\cake\blog\config\database.php
Wrote `c:\xampp\htdocs\cake\blog\config\database.php`

3. DBファイルを作成し、テーブルとテストデータを作成する

DBの作成先: {インストールディレクトリ}/blog/db/blog.sqlite3

DBファイルを開いて、下記SQLを実行する。

-- テーブルを作成する
CREATE TABLE posts (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title TEXT,
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

-- テストデータを流し込む
INSERT INTO posts (title,body,created) VALUES ('The title', 'This is the post body.', datetime("now", "localtime"));
INSERT INTO posts (title,body,created) VALUES ('A title once again', 'And the post body follows.', datetime("now", "localtime"));
INSERT INTO posts (title,body,created) VALUES ('Title strikes back', 'This is really exciting! Not.', datetime("now", "localtime"));

4. database.phpを編集する

編集するファイル: {インストールディレクトリ}/blog/config/database.php

<?php
class DATABASE_CONFIG {

    var $default = array(
        'driver' => 'sqlite3',
        'database' => 'C:/xampp/htdocs/cake/blog/db/blog.sqlite3',
    );
}
?>

ここまでやったら、http://localhost/cake/blog にアクセスして、「Sweet, “Blog” got Baked by CakePHP!」という画面が出ることを確認します。画面に赤か黄色で示されるエラーがあれば、どこか間違っているので直してください。

※画像やCSSが表示されない場合、.htaccessの設定が違う場合があります。下記を手がかりに解決してください。

5. Model/View/Controllerをbakeする

チュートリアルだと「/cake/console/にPATHを設定しろ」と書いていますが、面倒なのでやってません。

コンソールから、下記のように入力してbakeします。

cd c:/xampp/htdocs/cake/blog
../cake/console/cake bake

この後、何をするか対話式ダイアログで聞かれるので、「M」→「C」→「V」の順に作成してください。(Controllerより先にViewを作ろうとすると「まだControllerがないから作れません」と言われます)

6. 完成

http://localhost/cake/blog/posts にアクセスすると、画面ができあがってます。

SQLite3で現在時刻をINSERTする

このエントリーをはてなブックマークに追加
はてなブックマーク - SQLite3で現在時刻をINSERTする
Share on Facebook

datetime(‘now’, ‘localtime’)を使う。

INSERT INTO posts (body, created, modified)
VALUES ('ウソ文章', datetime('now', 'localtime'), datetime('now', 'localtime'));

なお、この値はスキーマ定義時にDEFAULT値に設定することなどもできるようです。

参考

SQLite3 での日付処理

Markdown記法で連番リストを無視したい

このエントリーをはてなブックマークに追加
はてなブックマーク - Markdown記法で連番リストを無視したい
Share on Facebook

Markdown記法では、行頭に「1.」「2.」などと書くと、勝手に連番リストにされてしまいます。

それが嫌な場合は、「999\.」というように、「\.」でエスケープしてやるとリストになりません。ただのパラグラフになります。

参考

Daring Fireball: Markdown Syntax Documentation