TRAJOIN is an Application to Translate symfony documents Jointly.

home > 1.2/cookbook/en > model_unit_testing.txt

[1] Edit ↑TOP

モデルの単体テストを行う


[2] Edit ↑TOP

PropelもしくはDoctrineモデルのための単体テストを書くのは極めて簡単です。 このチュートリアルでは、モデルのためのベターなテストを書くための重要なティップスとベストプラクティスを学びます。


[3] Edit ↑TOP

データベースの接続


[4] Edit ↑TOP

Propelのモデルクラスをテストするには、データベースが必要です。開発のために利利用しているものは既にありますが、 テストのために常に専用のものを作っておくことは良い習慣です。


[5] Edit ↑TOP

すべてのテストはtest環境の元で実行されるので、config/databases.yml設定ファイルを編集してtest環境のためのデフォルトの設定を上書きする必要があるだけです:


[6] Edit ↑TOP

test:
  propel:
    param:
      dsn:  mysql:dbname=myproject_test;host=localhost

dev:
  # dev configuration

all:
  propel:
    class: sfPropelDatabase
    param:
      dsn:        mysql:dbname=myproject;host=localhost
      username:   someuser
      password:   somepa$$word
      encoding:   utf8
      persistent: true
      pooling:    true
      classname:  PropelPDO

[7] Edit ↑TOP

このケースにおいて、データベースの名前だけを変更しましたが、 データベースのエンジンを変更して例えばSQLiteを利用することもできます。


[8] Edit ↑TOP

データベースを設定したので、propel:insert-sqlタスクを利用することでテーブルを作ることができます:


[9] Edit ↑TOP
$ php symfony propel:insert-sql --env=test

[10] Edit ↑TOP

テストデータ


[11] Edit ↑TOP

テストのための専用データベースがあるので、単体テストを立ち上げるたびにテストデータ(フィクスチャ)をロードする方法が必要です。 これはテストを実行するたびにデータベースを同じ状態にしたいからです。


[12] Edit ↑TOP

sfDataクラスのおかげでこれを実現する方法はとても簡単です:


[13] Edit ↑TOP

$loader = new sfPropelData();
$loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');

[14] Edit ↑TOP

loadData()メソッドはディレクトリもしくはファイルを1番目の引数として取ります。 共通のfixturesディレクトリは次の通りです:


[15] Edit ↑TOP
test/
  fixtures/
    10_categories.yml
    20_articles.yml
    30_comments.yml

[16] Edit ↑TOP

すべてのファイルの接頭辞として数字が追加されていることに注目して下さい。 これはデータのロードの順番をコントロールするための簡単な方法です。プロジェクトにおいて後で、フィクスチャデータを差し込む必要がある場合、 既存の数字の間に好きな数字を入れることで簡単にできます:


[17] Edit ↑TOP
test/
  fixtures/
    10_categories.yml
    15_must_be_laoded_between_categories_and_articles.yml
    20_articles.yml
    30_comments.yml

[18] Edit ↑TOP

明敏な読者はsymfony bookではこれらのフィクスチャをdata/ディレクトリに設置することを推奨されているのに対して、筆者がフィクスチャをtest/ディレクトリに設置したことに注目するでしょう。これは本当に好みの問題ですが、筆者はフィクスチャをこれらの2つのディレクトリの中で編成することを好みます。フィクスチャを2つの異なるグループに分類できるからです:


[19] Edit ↑TOP
  • data/fixtures: アプリケーションを実際に稼働させるために必要なすべての初期データを含む
  • test/fixtures: (単体と機能)テストによって必要なすべてのデータを含む

[20] Edit ↑TOP

テストデータの一式が小さいときはこのシンプルなスキーマは立派に機能しますが、モデルが成長し、もっとたくさんのフィクスチャを持つようになったら、データベースの中でそれらをロードする時間が重大になる可能性があります。 ですので、テストデータのサブセットだけをロードする方法が必要です。これを行う1つの方法は主要な機能ごとにサブディレクトリを作ることでテストデータの下位分類を行うことです:


[21] Edit ↑TOP
test/
  fixtures/
    10_cms/
      10_categories.yml
      20_articles.yml
      30_comments.yml
    20_forum/
      10_threads.yml

[22] Edit ↑TOP

これでメインのfixturesディレクトリをロードする代わりに、テストしたいモデルクラスに対応したサブディレクトリの1つだけをロードできます。 しかし大抵の場合、ユーザーのように、共有データをロードすることも必要です:


[23] Edit ↑TOP
test/
  fixtures/
    00_common/
      10_users.yml
    10_cms/
      10_categories.yml
      20_articles.yml
      30_comments.yml
    20_forum/
      10_threads.yml

[24] Edit ↑TOP

このユースケースを簡単にするために、loadData()メソッドはディレクトリかつ/もしくはファイルの配列を取ることができます:


[25] Edit ↑TOP

// ユーザーとCMSのすべてのデータをロードする
$loader = new sfPropelData();
$loader->loadData(array(
  sfConfig::get('sf_test_dir').'/fixtures/00_common/10_users.yml',
  sfConfig::get('sf_test_dir').'/fixtures/10_cms',
));

[26] Edit ↑TOP

このコードによって10_users.ymlフィクスチャファイルはロードされたすべてのフィクスチャファイルは10_cmsディレクトリの中で見つかります。


[27] Edit ↑TOP

単体テストを書く


[28] Edit ↑TOP

専用データベースとデータベースを既知の状態にする方法があるので、Articleモデルのための単体テストを作りましょう。


[29] Edit ↑TOP

ファイルをブートストラップするPropelの典型的な単体テストは次の通りです:


[30] Edit ↑TOP

// 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()は特定のスラグにマッチする記事を返す');

[31] Edit ↑TOP

このスクリプトの内容は一目瞭然です:


[32] Edit ↑TOP
  • 単体テストに関して、ブートストラップファイルを含みます。

[33] Edit ↑TOP

    include(dirname(__FILE__).'/../../bootstrap/unit.php');

[34] Edit ↑TOP
  • test環境のための設定オブジェクトを作りデバッグ機能を有効にします:

    
    $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true);
    

    これによってPropelのクラスのオートローディングも初期化されます。


[35] Edit ↑TOP
  • データベースマネージャを作ります。これはdatabases.yml設定ファイルをロードすることでPropelの接続を初期化します:

    
    new sfDatabaseManager($configuration);
    

[36] Edit ↑TOP
  • sfPropelDataを利用することでテストデータをロードします:

[37] Edit ↑TOP

    $loader = new sfPropelData();
    $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');

[38] Edit ↑TOP
  • すべての準備が整ったので、モデルオブジェクトのテストを始めることができます。

[39] Edit ↑TOP

単体テストを書くことに慣れていないのであれば、最初は怖いかもしれません。


[40] Edit ↑TOP

筆者がテストする必要があることを知るためにいつも使ういくつかのティップスは次の通りです:


[41] Edit ↑TOP
  • 1回につき1つのクラスの1つのメソッドをテストする
  • 特定の1つの入力に対して、メソッドの出力が期待するものと同じであることをテストする
  • メソッドのコードを読み、存在するであろうすべてのビジネスルールをテストする
  • 明らかなことや別のメソッドによって行われることはテストしない

[42] Edit ↑TOP

筆者のテストファイルは同じパターンで常に構造化されます:


[43] Edit ↑TOP

// テストするメソッドでメッセージを出力する(->はインスタンスメソッドに対して、::はクラスメソッドに対して)
$t->diag('->methodName()');

// 1回につきシンプルなセンテンスで表現できる1つのことをテストする
// センテンスは常にメソッドの名前で始まる
// 動詞はしなければならないこと、どのように振る舞わなければならないことなどを表現する
$t->is($object->methodName(), 1, '->methodName()は引数が渡されていない場合は1を返す');

[44] Edit ↑TOP

コードカバレージ


[45] Edit ↑TOP

テストを書くとき、複雑なコードの条件をテストすることは忘れがちです。


[46] Edit ↑TOP

symfony 1.2はtest:coverageというコードカバレージをテストするための小型で手軽なタスクを搭載しています。


[47] Edit ↑TOP

ですので、筆者は特定のクラスに対してテストを書いた後に、すべてのテストを実行したことを確認するために常にtest:coverageタスクを起動させます:


[48] Edit ↑TOP
$ php symfony test:coverage test/unit/model/ArticleTest.php lib/model/Article.php

[49] Edit ↑TOP

最初の引数はテスト用のファイルもしくはディレクトリです。 2番目の引数はコードカバレージを知りたいファイルもしくはディレクトリです。


[50] Edit ↑TOP

どの行がカバーされていないのか知りたいのであれば、--detailedオプションを追加します:


[51] Edit ↑TOP
$ php symfony test:coverage --detailed test/unit/model/ArticleTest.php lib/model/Article.php

Comments

リポジトリからコピー完了 (2008-12-29 12:31:37 brtRiver)

Menu

Documentation



Latest Histories

[php] // test/uni ...
brtRiver(2009-02-09 13:27:03)
$ php symfony test:co ...
brtRiver(2008-12-29 12:31:23)
$ php symfony test:co ...
brtRiver(2008-12-29 12:30:58)
If you want to know which ...
brtRiver(2008-12-29 12:30:48)
The first argument is a t ...
brtRiver(2008-12-29 12:30:37)
So, after my tests are wr ...
brtRiver(2008-12-29 12:30:26)
symfony 1.2 comes with a ...
brtRiver(2008-12-29 12:30:16)
When you write tests, it ...
brtRiver(2008-12-29 12:30:06)
Code coverage ----------- ...
brtRiver(2008-12-29 12:29:55)
[php] // output a ...
brtRiver(2008-12-29 12:29:45)
My test files are always ...
brtRiver(2008-12-29 12:29:33)
* Test one method of a ...
brtRiver(2008-12-29 12:29:23)
Here are some tips I use ...
brtRiver(2008-12-29 12:29:11)
If you are not used to wr ...
brtRiver(2008-12-29 12:29:01)
* Now that everything i ...
brtRiver(2008-12-29 12:28:52)
[php] $lo ...
brtRiver(2008-12-29 12:28:44)
* We load our test data ...
brtRiver(2008-12-29 12:28:33)
* We create a database ...
brtRiver(2008-12-29 12:28:20)
* We create a configura ...
brtRiver(2008-12-29 12:28:02)
[php] inc ...
brtRiver(2008-12-29 12:27:24)

Untranslated