PropelもしくはDoctrineモデルのためのユニットテストを書くのは極めて簡単です。 このチュートリアルでは、モデル用のベターなテストを書くための 重要なティップスとベストプラクティスを学びます。
データベースの接続
Propelのモデルクラスをテストするには、データベースが必要です。 開発用のものが既にありますが、 テスト専用のものを常に作っておくことは良い習慣です。
すべてのテストはtest
環境下で実行されるので、
config/databases.yml
設定ファイルを編集して
test
環境用のデフォルト設定を上書きする必要があるだけです:
test: propel: param: database: myproject_test dev: # dev configuration all: propel: class: sfPropelDatabase param: datasource: propel phptype: mysql hostspec: localhost database: myproject username: someuser password: somepa$$word
このケースにおいて、データベースの名前だけを変更しましたが、 データベースのエンジンを変更してたとえばSQLiteを利用することもできます。
テストデータ
テスト用の専用データベースがあるので、ユニットテストを立ち上げるたびに テストデータ(フィクスチャ)をロードする方法が必要です。 これはテストを実行するたびにデータベースを同じ状態にしたいからです。
sfData
クラスのおかげでこれを実現する方法はとても簡単です:
$loader = new sfPropelData(); $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');
loadData()
メソッドは1番目の引数としてディレクトリもしくはファイルを受け取ります。
共通のfixtures
ディレクトリは次の通りです:
test/ fixtures/ 10_categories.yml 20_articles.yml 30_comments.yml
すべてのファイルの接頭辞として数字が追加されていることに注目してください。 これはデータをロードする順序をコントロールするための簡単な方法です。 プロジェクトで後からフィクスチャデータを差し込む必要がある場合、既存の数字の間に好きな数字を入れます:
test/ fixtures/ 10_categories.yml 15_must_be_laoded_between_categories_and_articles.yml 20_articles.yml 30_comments.yml
賢明な読者はsymfony bookではこれらのフィクスチャがdata/
ディレクトリに設置することを推奨されているのに対して、
筆者がフィクスチャをtest/
ディレクトリに設置したことに注目するでしょう。
これは本当に好みの問題ですが、筆者はこれらの2つのディレクトリの中でフィクスチャを編成することを好みます。
フィクスチャを2つの異なるグループに分類できるからです:
data/fixtures
: アプリケーションを実際に稼働させるために必要なすべての初期データを含むtest/fixtures
: (単体と機能)テストによって必要なすべてのデータを含む
テストデータの一式が小さいときはこのシンプルなスキーマは立派に機能しますが、
モデルが成長し、もっとたくさんのフィクスチャを持つようになったら、データベースの中でそれらをロードする時間が重大になる可能性があります。
ですので、テストデータのサブセットだけをロードする方法が必要です。 これを行う1つの方法は主要な機能ごとにサブディレクトリを作ることで下位のテストデータの分類を行うことです:
test/ fixtures/ 10_cms/ 10_categories.yml 20_articles.yml 30_comments.yml 20_forum/ 10_threads.yml
これでメインのfixtures
ディレクトリをロードする代わりに、
テストしたいモデルクラスに対応したサブディレクトリの1つだけをロードできます。
しかしたいていの場合、ユーザーのように共有データをロードすることも必要です:
test/ fixtures/ 00_common/ 10_users.yml 10_cms/ 10_categories.yml 20_articles.yml 30_comments.yml 20_forum/ 10_threads.yml
テストファイルもしくはディレクトリをロードするために、loadData()
メソッドを
何度も呼び出すことができます:
// load users and all the CMS data $loader = new sfPropelData(); $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures/00_common/10_users.yml'); $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures/10_cms');
これによって10_users.yml
フィクスチャファイルはロードされ
すべてのフィクスチャファイルは10_cms
ディレクトリの中で見つかります。
ユニットテストを書く
専用データベースを既知の状態にする方法があるので、
Article
モデル用のユニットテストを作りましょう。
ファイルをブートストラップするPropelの典型的なユニットテストは次の通りです:
// test/unit/model/ArticlePeerTest.php include(dirname(__FILE__).'/../../bootstrap/unit.php'); $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true); new sfDatabaseManager($configuration); $loader = new sfPropelData(); $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures'); $t = new lime_test(1, new lime_output_color()); $t->diag('::retrieveBySlug()'); $article = ArticlePeer::retrieveBySlug('the-best-framework-ever'); $t->is($article->getTitle(), 'The Best Framework Ever', '->retrieveBySlug()は特定のスラグにマッチする記事を返す');
このスクリプトの内容は一目瞭然です:
ユニットテストに関して、ブートストラップファイルを含みます。
include(dirname(__FILE__).'/../../bootstrap/unit.php');
test
環境のための設定オブジェクトを作り デバッグ機能を有効にします:$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true);
これによってPropelのクラスのオートローディングも初期化されます。
データベースマネージャを作ります。 これは
databases.yml
設定ファイルをロードすることでPropelの接続を初期化します:new sfDatabaseManager($configuration);
sfPropelData
を利用することでテストデータをロードします:$loader = new sfPropelData(); $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');
すべての準備が整ったので、モデルオブジェクトのテストを始めることができます。
ユニットテストを書くことに慣れていないのであれば、最初は怖いかもしれません。
筆者がテストする必要があることを知るためにいつも使ういくつかのティップスは次の通りです:
- 1回につき1つのクラスの1つのメソッドをテストする
- 特定の1つの入力に対して、メソッドの出力が期待するものと同じであることをテストする
- メソッドのコードを読み、存在するであろうすべてのビジネスルールをテストする
- 明らかなことや別のメソッドによって行われることはテストしない
筆者のテストファイルは同じパターンで常に構造化されます:
// テストするメソッドでメッセージを出力する(->はインスタンスメソッドに対して、::はクラスメソッドに対して) $t->diag('->methodName()'); // 1回につきシンプルなセンテンスで表現できる1つのことをテストする // センテンスは常にメソッドの名前で始まる // 動詞はしなければならないこと、どのように振る舞わなければならないことなどを表現する $t->is($object->methodName(), 1, '->methodName()は引数が渡されていない場合は1を返す');
This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.