DB2 と Ruby on Rails: 第 3 回 DB2 と Ruby on Rails によるテスト

DB2 に適用する Ruby on Rails の組み込みテスト機能と共通 DB2 診断ツール

アジャイル・アプリケーション開発で重要となる要件は、リグレッション・テストを確実にするインクリメンタル方式のコード統合です。Ruby on Rails フレームワークはこの作業全体を今までになく容易に行えるようにします。Rails はよく知られた RAD (Rapid Application Development) 環境と同じように単体テストと機能テストをサポートするだけでなく、インフラストラクチャー、ディレクトリー、テスト・ファイル、そしてテスト・ケースの大部分を賢いテンプレートによって自動的に生成するからです。連載「DB2 と Ruby on Rail」の第 3 回では、Rails の組み込みテスト・フレームワークを第 2 回のデモ用サンプルTeam Room に適用します。

John Chun, DB2 Advanced Support Specialist, IBM 

John Chun は、アプリケーション開発およびツールの分野で活躍する DB2 Advanced Support チームのスペシャリストです。IBM DBT Toronto Lab でこれまで 7 年間、Java、C、C++、Perl、REXX、C# をはじめとする各種言語での DB2 アプリケーション問題の解決に取り組んできました。DB2 CLI および OLEDB ドライバー、そして .NET データ・プロバイダー関連の多数のプロジェクトにも参加しています。彼は DB2 認定ソリューション・エキスパート、認定 Websphere アドミニストレーターでもあります。



Alex Pitigoi, Advisory Software Engineer, IBM

Alex Pitigoi は、IBM Toronto Lab の顧問ソフトウェア・エンジニアです。1998年以来、Web 技術とデータベース管理を専門に、情報管理の分野でのさまざまなソフトウェア開発プロジェクトに携わっています。最近では、SQLModel プロジェクトの開発を指揮しました。この開発は現在、Eclipse Data Tools プロジェクトならびに複数の IBM データ・サーバーで、データベース管理 Web ツールの全体的アーキテクチャーに組み込まれています。彼は DB2 Satellite Administration Center、IBM Express Runtime にも取り組んでおり、DB2 を対象とした最初の Web Tools セットの開発も主導しています。現在重点的に取り組んでいるのは、新しいオープン・ソース技術 (Ruby、Python、PHP) へのIBM データ・サーバー対応化です。



Christine Law, DB2 Advanced Support Specialist, IBM 

Christine Law は、IBM Toronto Lab のシニア DB2 スペシャリスト兼 IBM 認定エキスパートです。JDBC、SQLJ、ストアード・プロシージャー、そして組み込みSQL を専門とする彼女は、さまざまなプログラミング言語とスクリプト言語を使った Linux、UNIX、Windows プラットフォームでの広範なアプリケーション開発経験を持っています。最近では、AJAXや Ruby などのオープン・ソース技術にも関心を寄せています。



Naomi Ngan, Senior Software Engineer, Autonomy

Naomi Ngan は 2000年、カナダの University of Toronto でコンピューター・サイエンスと統計学の優等卒業学位を終了しました。卒業と同時に IBM に入社した彼女は、アプリケーション開発環境における IBM DB2 RDBMS 製品の欠点および問題の解決を担当しました。約 4 年間 IBM に在籍した後は UCSF の Ernest Gallo Clinic and Research Center に移り、バイオインフォマティクス・ソフトウェアの開発に携わっています。これには、Linux および Windows プラットフォームの XML 環境内でのデータベース・オブジェクト、JSP、Java スタンドアロン・アプリケーション、およびストアード・プロシージャーの設計と開発も含まれます。現在 Autonomy Corporation のシニア・ソフトウェア・エンジニアとして取り組んでいるのは J2EE エンタープライズ・ソフトウェアの開発です。DB2 のアプリケーションおよびツールに関する深い知識を持つ彼女は、DB2、XML、WebSphere、Java/J2EE の分野で数々の IBM および Sun 開発者認定を取得しています。



2007年 6月 21日

はじめに

連載「DB2 と Ruby on Rail」の第 1 回では IBM_DB Ruby ドライバー、Rails マイグレーション、そして Team Room アプリケーションについて説明し、続く第 2 回では Rails アプリケーションで DB2® pureXML™ サポートを利用するために第 1 回で作成した Team Room アプリケーションを拡張する手順を説明しました。

Team Room アプリケーションが拡張され、複雑さを増してきたところで、アプリケーションが期待通りに動作することを確かめなければなりません。そこで当然、次のステップとなるのは Team Room アプリケーションのテストです。この第 3 回目の記事では、DB2 on Rails 環境ではテストがどのようにして行われるのか、そして Ruby on Rails フレームワークではテストをいかに簡単に作成できるかを紹介します。

Rails フレームワークは、テスト・サポートが組み込まれているおかげで、Web アプリケーションをテストするのに便利な手段となります。Rails でテストを作成する時機は、コア・アプリケーションができあがって実行できるようになってからでも、アプリケーションを作成しながらでも、さらにはアプリケーションを作成する前 (テスト主導型開発 (TDD) と呼ばれます) でも構いません。

Team Room アプリケーションのテスト

新規 Rails プロジェクトを作成すると、Rails によって自動的にテスト・インフラストラクチャーが生成されます。Rails の Team Room プロジェクトのディレクトリーを調べると、D:\rails\teamroom\ の下には \test サブディレクトリーがあり、\test の下には 5 つのディレクトリーと 1 つのヘルパー・ファイルが作成されていることがわかります。

リスト 1. test ディレクトリーの内容
/unit
/functional
/fixtures
/integration
/mocks
test_helper.rb

すべてのテストは /test ディレクトリーに入れ、それぞれのテストをその特性と機能に応じて 5 つのサブディレクトリーのいずれかに配置してください。以下に、/test ディレクトリーに含まれる各コンポーネントについて説明します。

unit (単体テスト)

Rails では、モデルのテスト用に作成されたテストは単体テストと呼ばれます。通常はモデルごとに 1 つの単体テストを作成します。単体テストでは、モデルを壊す可能性のあるすべてのものをテストしてください。基本テストには検証コードとアサーションのテスト、そして CRUD (Create、Read、Update、Delete) といったデータベース操作のテストを含める必要があります。

functional (機能テスト)

コントローラーのテスト用に作成されたテストは機能テストと呼ばれます。機能テストは単体テストより上位レベルでアプリケーションをテストするためのものです。この場合も同じく、使用するコントローラーごとに 1 つの機能テストを作成します。機能テストでは例えば、Web ページへのリクエストが正常に完了するか、正しいページにリダイレクトされるか、認証が適切に行われるか、そして特定のアクションに対して正しい応答が行われるかをテストします。

fixtures (フィクスチャー)

フィクスチャーはモデル内のコンテンツを指定します。フィクスチャーにはデータを指定し、単体テストを実行するときにそのデータをロードするように Rails に指示してください。これはいわば、DB2 でのインポートまたはロード関数のようなものです。

mocks (モック)

モックとは、制御の及ばない外部システムとの相互作用ではなく、Rails プロジェクトのスコープ内にある重要な機能のテストに専念できるようにするための「偽物の」オブジェクトのことです。例えば、Team Room アプリケーションでは新しい文書が特定のサブジェクトに追加されると、そのサブジェクトをサブスクライブしているユーザーに E メールで新規文書の追加が通知されます。このアプリケーションにはユーザーによって提供された E メールが実際に有効な E メール・アドレスであるかどうかを検証する E メール検証システムが用意されていないため、ユーザーの E メール・アドレスの有効性についての確認は、E メール・アカウント・プロバイダーやドメイン・サーバーからのエラー・レポートに依存しています。モック・テスト・メソッドを作成すれば、外部ネットワークにアクセスしたり、E メール検証システムを準備したりしなくても、Team Room の通知機能をテストすることができます。

integration (結合テスト)

結合テストとは、複数のコントローラーとアクションにまたがるテストのことです。その名前が示唆するように、このテストではさまざまなコントローラーとアクションが期待通りに連動するかどうかを確認します。結合テストは Rails アプリケーションに含まれる各種コンポーネント間の上位レベルでの相互作用を扱うため、通常はより総合的なテストとなります。

test_helper.rb

test_helper.rb ファイルには、複数のテスト・ケースに共通する多くのデフォルト Rails 動作が設定されます。例えば test_helper.rb では、Rails がテストの際に (database.yml ファイルで構成される) テスト・データベースを使用するように、RAILS_ENV 環境変数が「test」に設定されています。すべての単体テストは、Rails の test_helper.rb ファイルをロードします。また、test_helper.rb にはカスタム・アサーションを定義することも可能です。定義されたカスタム・アサーションは、同じ Rails アプリケーション内で複数のテストが共有し、使用することができます。

さまざまなデータベース環境のセットアップ

連載「DB2 と Ruby on Rails」の第 1 回では、DB2 XMLDB 開発データベースに接続するために D:\rails\teamroom\config\database.yml ファイルを編集しました。YAML ファイルの「development」セクションの下には「test」と「production」という追加セクションがありますが、前回はこの 2 つのセクションには明示的に入力していません。それぞれの名前が示すように、「test」はテスト・データベース環境を指定するセクションで、「production」は実動データベース環境を指定するセクションです。現実に、開発環境とテスト環境で同じデータベースを指定する場合がありますが、実動環境は、開発データベースやテスト・データベースとは明らかに異なります。それでは早速、TESTDB テスト・データベースを作成し、database.yml ファイルにテスト環境をセットアップしてみましょう。

以下のコマンドを実行して TESTDB データベースを作成します。

db2 create db testdb using codeset utf-8 territory us

database.yml を以下のように編集します。

リスト 2. database.yml ファイル
# IBM DB2 Database configuration file
#
# Install the IBM DB2 driver and get assistance from:
# http://www.alphaworks.ibm.com/tech/db2onrails
development:
  adapter:      ibm_db
  database:     xmldb
  username:     user
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      devuser
  workstation:  devbox
# == remote TCP/IP connection (required when no local database catalog entry available)
# host:         bigserver     // fully qualified hostname or IP address
# port:         50000         // data server TCP/IP port number

test:
  adapter:      ibm_db
  database:     testdb
  username:     testuser
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      testuser
  workstation:  testbox

production:
  adapter:      ibm_db
  database:     proddb
  username:     produser
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      produser
  workstation:  prodbox

テストを行うためには、テスト・データベースと開発データベースの表構造が確実に同じになるようにしなければなりません。そこで、テスト環境の作成を支援する Rake コマンドを使用すれば、スキーマをロードするための DDL スクリプトを管理する必要がなくなります。rake --tasks コマンドを実行すると、テスト・データベースを作成、またはこれを空にするための関連コマンドがいくつか表示されます。

リスト 3. rake --tasks の出力
rake db:test:clone             # Recreate the test database from the current 
                               # environment's  database schema
rake db:test:clone_structure   # Recreate the test databases from the development 
                               # structure
rake db:test:prepare           # Prepare the test database and load the schema
rake db:test:purge             # Empty the test database

注 1:rake db:test:* タスクを DB2 と連動させるには、http://db2onrails.com/ に記載されたステップを参照して schema_dumper.rbdatabases.rake を修正してパッチを当ててください。

古いファイルをバックアップした上で、以下のディレクトリーに用意された 2 つの新規ファイルと置き換えてください。

<ruby_path>\lib\ruby\gems\1.8\gems\activerecord-1.15.3\lib\active_record\schema_dumper.rb

<ruby_path>\lib\ruby\gems\1.8\gems\rails-1.2.3\lib\tasks\databases.rake

データベースに表を作成するには、rake db:test:prepare コマンドを使用します。このコマンドを実行すると、開発データベースのスキーマがテスト・データベースに複製されます。

リスト 4. rake db:test:prepare
D:\rails\teamroom> rake db:test:prepare
(in D:\rails\teamroom)

これで、testdb データベースに接続して、すべての表が正常に作成されたことを確認できます。

リスト 5. testdb に含まれる表
D:\rails\teamroom>db2 list tables for schema teamroom

Table/View                      Schema          Type  Creation time
------------------------------- --------------- ----- --------------------------
DOCUMENTS                       TEAMROOM        T     2007-06-05-11.21.46.343002
SCHEMA_INFO                     TEAMROOM        T     2007-06-05-11.21.48.306001
SUBJECTS                        TEAMROOM        T     2007-06-05-11.21.46.633003
SUBJECTS_SUBSCRIPTIONS          TEAMROOM        T     2007-06-05-11.21.47.004000
SUBSCRIPTIONS                   TEAMROOM        T     2007-06-05-11.21.47.194005
USERS                           TEAMROOM        T     2007-06-05-11.21.47.595003
XML_CONTENTS                    TEAMROOM        T     2007-06-05-11.21.47.955001

  
  7 record(s) selected.

テスト・データベースにはすべての表が定義されたので、テスト・データをロードする準備は万端です。テスト・データのロードは SQL の挿入またはインポートによって手動で行うこともできますが、この後説明するように、テキスト・フィクスチャーを利用すると面倒を省けます (同時に、管理もしやすくなります)。

Ruby の組み込み単体テスト

注 2: クラス宣言での「<」記号は、記号の左側に宣言されたクラスが右側で指定されたクラスのサブクラスであることを示します。

Ruby (1.8 以降) に付属している重要なライブラリーの 1 つとして挙げられるのは、test/unit です。Ruby テスト・スクリプトを作成するには以下の手順に従います。

  1. .rb ファイルの先頭に「"require 'test/unit'"」を指定します。この文によって、Test::Unit モジュールがロードされます。
  2. require 文の後に、このクラスが Test::Unit::TestCase のサブクラスであることを示すクラス宣言を追加する必要があります (リスト 6 を参照)。
リスト 6. 単体テストのための「"require 'test/unit'"」の指定
require 'test/unit'

class SubTestCase < Test::Unit::TestCase
  ...
end

Team Room アプリケーション・パスの test\unit サブディレクトリーには .rb ファイルがあります。これらのファイルは、scaffold が生成されたことから作成されたすべてのモデル・オブジェクトに対応し、ファイルのオブジェクト名には「_test」が追加されます。

test\unit サブディレクトリーの下にテスト・スクリプトを作成するときには、require 'test/unit' 文を指定する必要はありません。既存の require 文で参照される test_helper.rb ファイルに必要な情報が既に含まれているからです。

リスト 7. test\unit パスのリスト
D:\rails\teamroom\test\unit>dir
 Volume in drive D has no label.
 Volume Serial Number is C8D3-5819

 Directory of D:\rails\teamroom\test\unit

06/05/2007  09:27 AM    <DIR>          .
06/05/2007  09:27 AM    <DIR>          ..
06/05/2007  09:27 AM               208 customer_info_test.rb
05/31/2007  07:42 PM               199 document_test.rb
06/02/2007  05:14 PM               251 subjects_subscription_test.rb
05/31/2007  07:42 PM               315 subject_test.rb
05/31/2007  07:42 PM             1,211 subscription_mailer_test.rb
05/31/2007  07:42 PM               207 subscription_test.rb
05/31/2007  07:42 PM               191 user_test.rb
05/31/2007  07:42 PM               204 xml_content_test.rb
               8 File(s)          2,786 bytes
               2 Dir(s)  15,359,959,040 bytes free

「test_」プレフィックスが付いたメソッド

Ruby では、すべてのテスト・メソッド名は「test」という 4 文字で始まります。この 4 文字で始まっていない場合、テスト・フレームワークはテストとして認識しません。「test」プレフィックスのないメソッドは通常の Ruby メソッドとして扱われるため、テスト・フレームワークによって自動的に実行されることはありません。

リスト 8. 一般的な単体テスト
require File.dirname(__FILE__) + '/../test_helper'

class Sub_Test < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

ここで、sub_test.rb という名前を付けてファイルを保管します。このファイルを実行すると、以下の内容が出力されます。

リスト 9. sub_test.rb の実行
D:\rails\teamroom\test\unit>ruby sub_test.rb
Loaded suite sub_test
Started
.
Finished in 0.631 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

注 3: 上記のテストによる出力結果は「.」です。Rails では「.」は合格を意味し、「F」は失敗 (予期しない結果によって失敗したアサーションが原因)、「E」はエラー (通常は Ruby の問題) を意味します。

1 つのアサーションを実行する上記の単純なテスト・シナリオは、「assert true」文により成功したことがわかります。アサーションとは、すなわちテストのことです。次のセクションで説明するように、Rails アプリケーションのテストには数々の assert 文を使用することができます。

Rails では、script/generate model、script/generate controller、script/generate scaffold によって作成されたすべてのモデルおよびコントローラーが、対応するテスト・スタブを test サブディレクトリー内に作成します。これらのテスト・スタブを使用する場合には、「"require 'test/unit'"」を指定する必要はありません。

アサーション

アサーションはアプリケーションまたはモジュールの事前/事後の状態を検証し、有効な状態であることを確認します。Test::Unit アサーションは基本パターンに従うため、期待される結果が最初のパラメーターに、実際の結果が 2 番目のパラメーターに入ります。期待される値と実際の値が一致しない場合、何が問題だったのかを説明するメッセージが生成されます。

中心的な Ruby アサーションのリストについては、Test::Unit::Assertions を参照してください。

他にも Rails 開発者が使用できる数多くのアサーションについては、 Module Test::Unit::Assertions Information を確認してください。

アサーションは前述したように、有効な状態を確認するために使用されます。それでは有効な状態とは一体何によって定義されるのでしょう。

有効な状態のほとんどは、コード内の検証結果に左右されます。

検証を使用する

入力データは、データベースに保管する前でも検証することができます。それにはカスタム検証を定義するか、あるいは一連の標準検証を使用します。属性の名前、長さ、フォーマットのチェックなどの標準的な検証を行うには、ActiveRecord に用意された検証を利用することができます。

注 4: ActiveRecord の検証メソッド名はすべて「validates_」で始まり、1 つ以上の属性名または 1 つ以上の検証オプション、あるいはその両方を取ります。

検証メソッドをモデルに追加すると、オブジェクトがデータベースに保管される前に検証が行われます。オブジェクトは検証の結果、合格するとデータベースに書き込まれます。検証の結果が失敗だった場合は、そのオブジェクトに対して失敗した検証メソッドに関連するエラー・メッセージがエラー・リストに追加されます。このリストを表示すれば、ユーザーが適切な処置を講じて失敗を解決することができます。

この記事では、ActiveRecord の一連のバリデーターを使ってモデルを検証する例を紹介します。

サンプル・アプリケーション Team Room では、例えばサブジェクトについて以下の検証を行う必要があるとします。

  • サブジェクトに関連付けられたすべての文書オブジェクトが有効なオブジェクトであること
  • サブジェクト名が空でないこと
  • サブジェクト名の長さが 20 文字以下であること

上記の検証が確実に行われるようにするためには、標準検証メソッドのいくつかを利用することができます。app\models\subject.rb に追加する検証は以下のとおりです。

リスト 10. app\models\subject.rb に含まれる検証
  validates_associated      :documents
  validates_associated      :subscriptions				
  validates_presence_of     :name
  validates_length_of       :name,
                            :maximum => 20,   # maximum 20 characters
                            :too_long => "is too long, maximum 20 characters"
  validates_numericality_of :size,            # value is numeric and only integer accepted
                            :only_integer => true,
                            :greater_than => 0                             
  validates_presence_of     :description
  validates_length_of       :tag,
                            :maximum => 10,   # maximum 10 characters
                            :too_long => "is too long, maximum 10 characters"

「validates_associated」メソッドによって、サブジェクトに関連付けられたすべての文書が有効であることをチェックし、「validates_presence_of」メソッドによってサブジェクト名が空でないことをチェックします。

サブジェクト名の長さが 20 文字以下であることをチェックするには、「validates_length_of」メソッドを使います。「maximum」は、サブジェクト名の最大サイズを指定するための構成オプションです。サブジェクト名が 20 文字を超えている場合に生成されるエラー・メッセージを指定する「too_long」オプションは、「message」オプションに代わって使用されます。「too_long」メッセージのデフォルトは「is too long (maximum is %d characters)」(「が長すぎます (最大 %d 文字です)」) です。

注 5: validates_size_of メソッドは validates_length_of メソッドの別名です。

これで、サブジェクトの入力データは上記に設定したルールに従って検証されるようになります。次に、ユーザーが問題のあるデータを入力した場合には、その入力データを修正できるようにエラー・メッセージのリストがユーザーに表示されるようにしなければなりません。

この場合に役立つ ActionView メソッドには以下のものがあります。

  • error_message_on - 指定されたオブジェクト属性に関するエラー・ストリングを返します。
  • error_messages_for - 指定されたオブジェクトのエラー・リストを返します。

これらのメソッドについての詳細を調べるには、ここをクリックしてください。

Team Room の場合、新しいサブジェクトに対して入力されたすべての入力データのエラー・メッセージを一覧表示するには、app\views\subjects\_form.rhtml の先頭に以下のメソッドを追加します。

リスト 11. 入力データのエラー・メッセージ
<%= error_messages_for 'subject' %>

このメソッドを呼び出すと、すべてのエラー・メッセージ (存在する場合) が含まれる div が返されるので、簡単にエラー・メッセージを表示することができます。

図 1. 長すぎるサブジェクト名を入力した場合のエラー・メッセージ
エラー・メッセージ

ここをクリックすると、クラス・スコープ内に定義可能な Active Record のモデル検証のリストを見ることができます。

これで、サブジェクト・モデル検証の準備ができたので、アサーションを使って上記の検証を実際にテストすることができます。

アサーションを使用する

これからアサーションを使用して、SUBJECTS 表に関連付けられたモデルをテストします。SUBJECTS 表に関連付けられたモデルのなかに、表に必要な情報が有効な長さで入力されていることを確認するというものがあります。このような検証は、バックエンド・データベースに不必要な負荷をかけない上で重要です。テストする検証内容については、\app\models\subject.rb に概略が示されています (リスト 10 を参照)。

「test_invalid_with_empty_values」メソッドは、値が空であるというエラー条件のアサーションを説明する単純な例です。この「test_invalid_with_empty_values」メソッドで、サブジェクト値が空の場合にエラーになるかどうかをテストすることにします。

リスト 12. テスト・メソッド「test_invalid_with_empty_values
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase

# Tests validation failure using empty values
  def test_invalid_with_empty_values
    subject = Subject.new
    assert !subject.valid?
    assert subject.errors.invalid?(:name)
    assert subject.errors.invalid?(:size)
    assert subject.errors.invalid?(:description)
  end
  ...

有効なサブジェクト・データをアサートする 2 番目のテストと、誤った長さの TAG を指定した場合に期待されるエラー・メッセージが表示されるかどうかを調べる 3 番目のテストを追加します。

この時点で、test/unit/subject_test.rb には以下の 3 つのテスト・メソッドがあるはずです。

リスト 13. subject_test.rb のテスト・メソッド
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase

# Tests validation failure using empty values
  def test_invalid_with_empty_values
    subject = Subject.new
    assert !subject.valid?
    assert subject.errors.invalid?(:name)
    assert subject.errors.invalid?(:size)
    assert subject.errors.invalid?(:description)
  end
  
# Tests validation using valid data
  def test_valid_subject
    subject = Subject.new(:name=>"Perl",
                          :size=>2,
                          :description=>"test",
                          :tag=>"123456789")
    assert subject.valid?
  end

# Tests invalid TAG length  
  def test_invalid_tag_length
    subject = Subject.new(:name=>"AJAX",
                          :size=>3,
                          :description=>"test",
                          :tag=>"abcdefghijklm")
    assert !subject.valid?
    assert_equal "is too long, maximum 10 characters",subject.errors.on(:tag)
  end 
end

上記の単体テストが成功すると、以下の内容が出力されます。

リスト 14. サブジェクト・単体テストの実行
D:\rails\teamroom>ruby test/unit/subject_test.rb
Loaded suite subject_test
Started
...
Finished in 13.891 seconds.

3 tests, 7 assertions, 0 failures, 0 errors

注 6: SubjectTest は 1 つの TestCase です。Test::Unit::TestCase から継承されたこのクラスには、1 つ以上のテストを含められます。このようなテスト・ケースの集合は TestSuit と呼ばれ、各 TestCase には多数のアサーションを含めることが可能です。

出力にある 3 つのテストとは subject_test.rb ファイルに定義された 3 つのテスト・メソッドのことで、7 つのアサーションとはこのファイルに含まれるすべての assert 文のことです。そして「.」は注 3 で前述したように、合格したことを意味します。

上記の subject_test.rb の例には、リスト 15 の 7 つのアサーションが含まれます。

リスト 15. subject_test.rb 内のアサーション
assert !subject.valid?
assert subject.errors.invalid?(:name)
assert subject.errors.invalid?(:size)
assert subject.errors.invalid?(:description)

assert subject.valid?

assert !subject.valid?
assert_equal "is too long, maximum 10 characters",subject.errors.on(:tag)

subject_test.rb に含まれる 3 つのテストは、リスト 16 に示すとおりです。

リスト 16. subject_test.rb 内のテスト・メソッド
test_invalid_with_empty_values
test_valid_subject
test_invalid_tag_length

フィクスチャーを使用したテスト

Rails アプリケーションをテストするには、ブラウザーに手作業で値を入力する代わりにテスト・フィクスチャーを使ってサンプル・データをデータベース表にロードするという方法もあります。フィクスチャーを使用すると、事前定義されたデータ値をデータベースに入力してからテストを実行することができます。フィクスチャーの形式は、YAML またはコンマ区切り値 (CSV) のいずれかです。フィクスチャーはデータベースには依存しないため、同じ 1 つのフィクスチャーを使ってさまざまなデータベース・システムに対して Rails アプリケーションをテストすることができます。

これからフィクスチャーを使って、Team Room アプリケーションのサブジェクトとサブスクリプションをテストします。このテストでは YAML フィクスチャーを使用するので、/test/fixtures ディレクトリーに配置された subjects.yml ファイルを編集します。Rails はサブジェクトの単体テストを生成するときに、対応する空の subjects.yml ファイルを作成します。ruby script/generate model を実行して新しいモデルを作成するとき、あるいは ruby script/generate scaffold を実行して scaffold を生成するときには、フィクスチャー・スタブが自動的に作成されて /test/fixtures ディレクトリーに配置されます。

注 7: フィクスチャー・ファイルの名前は、データベース内の基礎となる表の名前と一致していなければなりません。したがって、Team Room の SUBJECTS 表にデータをロードするには、サブジェクトのフィクスチャー・ファイルに subjects.yml という名前を付けてください。

注 8: YAML フィクスチャー・ファイルごとに、1 つのモデルのデータが含まれます

subjects.yml を編集して 24 の異なるサブジェクトを設定しました。/test/fixtures/subject.yml は、この記事の「ダウンロード」セクションに用意したサンプル・コードのなかにあります。それぞれのフィクスチャーには、コロンで区切られたキーと値のペアのリストが続く名前が付けられています。

subjects.yml のサンプル・コンテンツは以下のとおりです。

リスト 17. subjects.yml のサンプル・コンテンツ
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
photography:
  id: 1
  name: Photography
  size: 0
  description: Digital Photos, images
  tag: portraits

gardening:
  id: 2
  name: Gardening
  size: 0
  description: Gardening info
  tag: flowers

golf:   
  id: 3
  name: Golf
  size: 0
  description: Golf information, golf club, golf course discount available to members
  tag: golf

tennis:
  id: 4
  name: Tennis
  size: 0
  description: Tennis information, tennis club, tennis court bookings
  tag: tennis 

<etc ..>

Rails フィクスチャーのロード・メカニズムはすでに用意されているので、Rails にテスト・データをロードさせるためのコードを追加する必要はありません。ここではリスト 13 の /test/unit/subject_test.rb を変更して、フィクスチャーを使用してテストするためのコードを追加してあります。

リスト 18. /test/unit/subject_test.rb に含まれるフィクスチャー関連のエントリー
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase
  fixtures :subjects

  # count the number of subject fixtures
  def test_subject_fixtures
    assert_equal 24, Subject.count
  end
  ...  
  # Previous test methods and assertions are listed below
  ...
end

fixtures メソッドは、テスト・ケースに含まれる各テスト・メソッドを開始するたびに、指定されたモデル名に対応するフィクスチャーを自動的にロードします。デフォルトで、:subjects は Rails に ubjects.yml ファイルからサンプル・データをロードするように指示します。上記では、24 すべてのサブジェクトが正しくロードされることをテストするために「test_subject_fixtures」メソッドが追加されています。

ruby test/unit/subject_test.rb によってこのテストを実行すると、以下の内容が出力されます。

リスト 19. フィクスチャーを使用したサブジェクトの単体テストの出力
D:\rails\teamroom>ruby test/unit/subject_test.rb
Loaded suite test/unit/subject_test
Started
....
Finished in 3.15 seconds.

4 tests, 8 assertions, 0 failures, 0 errors

WEBrick サーバーを起動し、登録済みユーザーとしてログインしているという前提では、http://localhost:3000/subjects/list までブラウズすると、 サブスクリプション対象として 24 のサブジェクトが選択可能になっているはずです。

図 2. 設定されたサブジェクト・エントリーのスクリーンショット
サブジェクト・エントリー

場合によっては、ERb (Embedded Ruby) と動的フィクスチャーを併せて使用してフィクスチャー・データを生成しなければならないこともあります。最もありがちな例は、テストの実行に伴った実際のタイムスタンプを生成する場合です。この場合、Ruby コードを実行するには <% ... %> を使用します。さらに <%= ... %> を使用すれば、Ruby コードを実行できるだけでなく、その結果を表示することもできます。動的フィクスチャーと ERb の使い方については、この後の機能テストおよびパフォーマンス・テストの実行方法に関するセクションで説明します。

フィクスチャーを共有して多対多の関係をテストする

もう一歩高度なフィクスチャーの使い方としては、フィクスチャーを使ってサブジェクトとサブスクリプションとの関係をテストすることもできます。もちろん、http://localhost:3000/subjects/list までブラウズし、現行のログイン・ユーザーとしてサブスクライブしたい複数のサブジェクトにチェック・マークを付けてから Team Room のサブジェクトとサブスクリプションの関係をテストすることもできますが、多数のサブジェクトが関係する多くのユーザー・サブスクリプションをテストするとなると、この方法はかなり面倒になります。なぜなら、手動で多数のユーザー・サブスクリプションを作成した上で、ログインしてサブスクライブするサブジェクトにチェック・マークを付けるという作業を該当するユーザーごとに繰り返さなければならないからです。

それよりも効率的にモデル間の多対多の関係をテストする方法となるのは、結合表のデータが含まれる新しいフィクスチャーを作成することです。前回の第 2 回では、SUBJECTS_SUBSCRIPTIONS 表を作成して SUBJECTS 表と SUBSCRIPTIONS 表との多対多の関係を表し、データベースが確実に正規化されるようにしました。この SUBJECTS_SUBSCRIPTIONS 表にデータを入力するため、/test/fixtures/subjects_subscriptions.yml ファイルを以下のように編集します。

リスト 20. subjects_subscriptions.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
# Refer to /test/fixtures/subjects.yml for more information
# Subject ID 2 = Gardening
subscription160_gardening:
  subscription_id: 160
  subject_id: 2
 
# Subject ID 3 = Golf  
subscription160_golf:
  subscription_id: 160
  subject_id: 3

上記では、160 というサブスクリプション ID を持つユーザーが 2 つのサブジェクト、Gardening (ガーデニング) と Golf (ゴルフ) をサブスクライブしていることになっています。

サブジェクトとサブスクリプションの関係をテストするということは、つまり SUBJECTS 表と SUBSCRIPTIONS 表の両方のデータにアクセスするとともに、結合表 SUBJECTS_SUBCRIPTIONS にもアクセスしなければならないということです。したがって、/test/unit/subjects_subscription_test.rb テストでは 3 つすべてのフィクスチャーをロードする必要があります。

リスト 21. フィクスチャー
class SubjectsSubscriptionTest < Test::Unit::TestCase
   fixtures :subjects_subscriptions, :subjects, :subscriptions
   [...]
end

このように、フィクスチャーは複数のテスト・ケースでテスト・データを共有する必要がある場合に極めて役立ちます。フィクスチャーについての詳細は、Ruby on Rails 資料に記載されている Class Fixtures を参照してください。

モック・オブジェクトの使用

モックが役立つのは、外部システムへのアクセスを懸念することなくアプリケーションのコア機能をテストしたいという場合です。Team Room ではユーザー登録時に E メール・アドレスが正しい形式であることは検証しているものの、指定された E メール・アドレスが本当に存在するかどうかはまるでわかりません。E メール・アドレスが実際に有効なものであることを確認する唯一の方法は、指定された E メール・アドレスに E メールを送信することです。E メール・アドレスが有効ではなかったり、存在していなかったりすれば、E メール・プロバイダーからエラーが送られてくるからです。このようなサブスクリプション失敗の通知配信をテストする場合には、モックを使うことができます。

このテスト環境では、例えば E メール配信メソッドのモックを作成することができます。そこで、モックの作成対象となる配信メソッドを定義する email_provider.rb ファイルを /test/mocks/test ディレクトリーに作成します。モック・ファイルが配置されると、Rails はまず /test/mocks/test/email_provider.rb をロードし、それから /app/models/email_provider.rb の内容を調べます。

注 9: モック・ファイルの名前は、/app/models ディレクトリー内のモデルとまったく同じ名前でなければなりません。モック・ファイルに再定義するのはモックの対象メソッドだけです。対応するモック・ファイルに既存のすべてのメソッドを定義する必要はありません。

/test/mocks/test/email_provider.rb は以下のようになります。

リスト 22. フィクスチャー
require 'models/email_provider'
 
class EmailProvider
  def deliver(request)
    #Mock method
    :success
  end
end

モックを使用すれば、外部リソースにアクセスできなくても気にすることなく Team Room アプリケーションのコア機能のテストに専念することができます。

機能テスト: コントローラーのテスト

Rails アプリケーション・コントローラーのテストは、Web ブラウザーから送信されたすべてのリクエストがユーザー入力に基づいて期待通りに応答されると同時に、適切な状態変更が行われることを確実にすることを目的とします。そのためコントローラー内のアクションごとに、その特定のパス (URL) を使用するテスト・ケースが少なくとも 1 つは必要です。また、異なる状態をテストするために複数のテストが必要になる場合もあります (ユーザーが認証されている場合とそうでない場合でのアクションのアクセスなど)。

機能テストの一例として、ログイン・アクションと登録アクションに関係する UsersController のケースを検討します。関連する UsersControllerTest はモデル・クラスとコントローラー・クラスと併せて既に生成済みで、このテストにはセットアップとスタブ・メソッドの完全なテンプレートが含まれているので、実行パスを処理するのに必要なカスタマイズは最小限で済むことになります。

リスト 23. Users コントローラーのテスト
require File.dirname(__FILE__) + '/../test_helper'
require 'users_controller'

# Re-raise errors caught by the controller.
class UsersController; def rescue_action(e) raise e end; end

class UsersControllerTest < Test::Unit::TestCase
  fixtures :users

  def setup
    @controller = UsersController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new

    @first_id = users(:first).id
  end
  [...]
end

必須メソッドである setup は、機能テストに常に必要な 3 つのインスタンス変数を初期化します。この 3 つのインスタンス変数とはつまり、テスト対象の controller、HTTP ヘッダー情報とともにユーザー入力が含まれる request、そしてテンプレートからレンダリングされたデータとステータス・コードを含むresponse です。

コントローラーのテストで重要な点は、このシナリオのテスト・データを提供する関連フィクスチャーです。そしてこの特定のコントローラーが、フィクスチャーの動的な側面をどのように使用するかを理解する手掛かりとなります。認証済みのユーザーを伴う上記のテスト・ケースでは、暗号化されたパスワードを計算するために USERS 表に有効なエントリーを設定する必要があります。暗号化パスワードを別途計算してフィクスチャーで直接使用することもできますが、ビューと部分ビューで行われているテンプレートの置換によっても同じように機能するため、タスクが簡単になります。

リスト 24. Users フィクスチャー
<% SALT = "tstsalt" unless defined?(SALT) >
first:
  id: 141
  usertype:  usr
  firstname: Homer
  lastname:  Simpson
  extension: '9955'
  email:     homer@simpson.org
  userid:    homers
  salt: <%= SALT %>
  hash_passwd: <%= User.encrypt('secret' , SALT) %>

最初に行う test_index テスト・ケースは 2 つの異なるテスト・ケースに分ける必要があります。こうすると、ユーザーが認証された場合と認証されなかった場合のリクエストの処理をより正確に検証することができます。

リスト 25. Users コントローラーでのインデックス・アクションのテスト
  def test_index_without_user
    get :index
    assert_redirected_to :action => "login"
    assert_equal "Please sign-in" , flash[:notice]
  end

  def test_index_with_user
    get :index, {}, {:user_id => users(:first).id}
    assert_response 302
  end

このコントローラーのインデックス・アクションでは、ユーザーが認証されていなければ常にログイン・ページを表示することになります。このアクションを実行するには、テスト・ケースでブラウザーからの GET リクエストをシミュレートする必要があります。ActionController::Integration::Session によって定義される get メソッドがHTTP リクエストを生成するために取得するものは、URL と、アクションによって渡された HTTP パラメーターを表すハッシュ (上記の場合は空)、そしてセッションを設定するためのパラメーターのハッシュです。これらが揃えば、あとは特定のテスト・ケースを呼び出すだけです。

リスト 26. Users コントローラー・テストの実行
D:\rails\teamroom>ruby test\functional\users_controller_test.rb -n test_index_with_user
Loaded suite test/functional/users_controller_test
Started
.
Finished in 0.719 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

E メール通知とサブスクリプション確認のテスト

E メール通知をテストするには、単体テストおよび生成された E メールの内容の検証を行うだけでなく、機能テストも必要になります。機能テストによって、E メール通知が必要なときに常に送信されることが確実になるからです。そこで、第 2 回Action Mailer をベースに実装した E メール通知コントローラーを基に、スタブによる機能テストで生成した SubscriptionMailerTest を使用し、通知動作に関係するモデルに対して既に作成されているフィクスチャーを利用することにします。

E メール通知とサブスクリプション確認の単体テストを行うには、メーラー・テスト用ドライブにローカル E メール配信用の ActionMailer を構成し (delivery_method = :test)、さらにモデル・フィクスチャーを使って通知および確認を呼び出すために必要なオブジェクトをセットアップする必要があります。

リスト 27. メーラー機能テストのセットアップ
class SubscriptionMailerTest < Test::Unit::TestCase
  FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
  CHARSET = "utf-8"

  include ActionMailer::Quoting
  fixtures :users
  fixtures :subscriptions
  fixtures :subjects
  fixtures :documents

  def setup
    ActionMailer::Base.delivery_method = :test
    ActionMailer::Base.perform_deliveries = true
    ActionMailer::Base.deliveries = []

    @user = users(:first)
    @subscription = subscriptions(:one)
    @doc  = documents(:first)
    @subject = subjects(:ruby_on_rails)
    @subject.documents << @doc
    @subject.subscriptions << @subscription
  end
  [...]
end

このテスト実装はメーラー・クラスを使用して E メールの内容を作成しますが (create_notify)、SMTP サーバーに実際に送信することはしません。代わりにアサーションを使用して、本文に含まれる特定のセクションをはじめとするメッセージ内の生成された部分 (受信者、送信者、タイムスタンプなど) を検証します。

リスト 28. E メール通知のテスト
  def test_notify
    response = SubscriptionMailer.create_notify(@doc)

    assert_equal("TeamRoom new shared document notification", response.subject)
    assert_equal("teamroom@developerWorks.ibm.com", response.from[0])
    assert_equal(nil, response.to)
    assert_equal("homer@simpson.org", response.bcc[0])
    assert_in_delta(Time.now, response.date, 1.0)
    assert_match(/A new document DB2onRails-logo.gif/, response.body)
    assert_match(/subject: Ruby on Rails/, response.body)
  end

サブスクリプション確認 E メールのテストでも同じような方法で、メッセージの内容を作成してから検証が行われます。さらに E メーラーの機能テストを行うということは、E メールを配布するのではなく、E メール・メッセージをメモリー内配列に追加する (ActionMailer::Base.deliveries = []) というコントローラーのテスト・ケースを意味します。これにより、送信済み E メールの数を検証するだけでなく、各メッセージの内容を検証することもできます。

パフォーマンス・テスト

デフォルトでは unit、functional、integration の外部のテスト・ディレクトリーのリストにパフォーマンス・テストのテンプレートが生成されることはありませんが、開発者がアプリケーションのパフォーマンス特性をいくつか評価したいという場合もあります。開発のあまりにも早い段階からパフォーマンスに重点を置くのは賢明ではありませんが、開発プロセスが終わりのサイクルに達するまでには、容量計画に対処するためにも、以降のサイクルを繰り返すなかで速度が落ちてきた場合にリグレッション・テストを確実に行えるようにするためにもパフォーマンス・テストを行うようお勧めします。

他のテスト・カテゴリーと併せてパフォーマンス・テストを実装する方法について簡単に説明するために、次のシナリオについて検討することにします。そのシナリオとは、パフォーマンス・テストを比較的大規模なエンタープライズ環境にデプロイした場合に、Team Room アプリケーションに非常に大きな負荷がかかってしまうかもしれないいうシナリオです。これまでと同じく、まずはテスト・データから取り掛かります。というのも、比較的大量のテスト・データを初期化する際に、パフォーマンス・テストのためにのみロードされる一連のフィクスチャーを使えるようにするためです。

リスト 29. 大規模なデータ・テスト・セットの場合の動的ユーザー・フィクスチャー
<% SALT = "tstsalt" unless defined?(SALT) %>
<% 200.upto(1200) do |i| %>user_<%= i %>:
  id: <%= i %>
  usertype:  usr
  firstname: New<%= i %>
  lastname:  One
  extension: '9999'
  userid:    new<%= i %>
  email:     new<%= i %>@domain.net
  salt:      <%= SALT %>
  hash_passwd: <%= User.encrypt('secret' , SALT) %>
<% end %>

このパフォーマンス・テストは通常の使用シナリオに基づくという前提で、極限の負荷をかける大規模なデータ・テスト・セットにまで拡張します。つまり、テスト・コントローラーは通常の機能テストで使用するものとかなり似ていますが、このテスト・ケースでは特定の点が誇張されているということです。ここで、ユーザー・コントローラーと極限の負荷でテストされるログイン・アクションについて考えてみると、テスト・データがロードされる場所 (self.fixture_path) を除けばテスト・ケースのセットアップは通常の機能テストとまったく同じになります。

リスト 30. ユーザー・ログイン・パフォーマンス・テストのセットアップ
class UserLoginTest < Test::Unit::TestCase
  self.fixture_path = Pathname.new(File.dirname(__FILE__)).parent + 
                                   'fixtures' + 'performance'
  fixtures :users

  def setup
    @controller = UsersController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end
  [...]
end

多数の同時ユーザーに対する Team Room の処理能力をテストするには、フィクスチャーに定義されたすべてのユーザーを同時にログインさせ、さらにはこのアクションに最小限の時間制限を設けるという方法があります。リスト 31 ではユーザー・ログインをループで実装するとともに、Ruby の標準ライブラリー・ベンチマークを使って時間の推定値を収集するためにロガーがオフになっていることを確認しています。

リスト 31. ログイン・アクションの際のユーザー・コントローラーのパフォーマンス・テスト
  def test_1000_login
    @controller.logger.silence do
      user_count = 0
      elapsed_time = Benchmark.realtime do
        1200.downto(200) do |id|
          user = users("user_#{id}")
          post :login, :userid => user.userid, :password => 'secret'
          user_count += 1
        end
      end
      assert_equal 1001, user_count
      assert elapsed_time < 10.0
    end
  end

さらに現実的なパフォーマンス・テストのシナリオとしては、マーケティング・レポートを支援する一部の XML クエリーが挙げられますが、これについてはこのようなアプリケーションを実動に移行させたいという読者の演習として残しておくことにします。このパフォーマンス・テストを実装する上で重要な点は、通常の機能テスト・ケースと結合テスト・ケースを大規模なテスト・データ・セットと極限の負荷と組み合わせて使用するということです。この手法によって、パフォーマンスの劣化を抑止するための実装を極めて簡単かつ効果的に行うことができます。

Rake によるアプリケーション・テストの実行

ディレクトリー構造は、rails teamroom コマンドを使って Team Room アプリケーションの作成を始めたときに生成されました。さらに重要なことには、Rails gem に組み込まれた一連のユーティリティーは「フック」されています。Rails gem は、他のすべてのコンポーネント (そのうち最もよく目にするコンポーネントは ActiveRecordActionView の 2 つです) のシームレスな統合を裏で調整する、単純かつ簡単な処理を行うコンポーネントですが、それと同時に、Rails gem は強力な Rake gem ヘルパーを使って、このフレームワークを使用するすべての Web アプリケーションにありがちな一連の問題に対処します。

make ユーティリティーに相当する Ruby の Rake は、その事前定義されたタスク (make ターゲットに相当) によってマイグレーション、スキーマのロード/ダンプ、Rails gems の更新/フリーズ、そして文書の作成を手早く簡単に行えるようにするだけでなく、テストも手早く簡単に行えるようにします。Team Room アプリケーションのルート・ディレクトリーで rake --tasks を実行すると、この信頼性の高い支援機能、rake によって提供されるすべての事前定義タスクが表示されます。これはすべて Team Room アプリケーションにあらかじめ組み込まれたもので、ディレクトリー構造が作成され、Rake ファイルが作成されると、一連の事前定義タスクが含まれる tasks/rails ライブラリー・パスが共有されます。

単純な rake test コマンドで、それまでに作成された単体テストと機能テストのすべてを並行して実行することや、開発サイクルの中でこれらのテストを容易に呼び出すことができます。カテゴリー別にテストを実行することも可能で、Rails アプリケーションの標準ディレクトリー構造により、test/unit パスに定義されたテスト全体を難なくロードすることができます (リスト 32 を参照)。

リスト 32. Team Room アプリケーションのすべての単体テストの実行
D:\rails\teamroom>rake test:units
(in D:/rails/teamroom)
D:/ruby/bin/ruby -Ilib;test 
"D:/ruby/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" 
"test/unit/customer_info_test.rb" "test/unit/document_test.rb" 
"test/unit/subjects_subscription_test.rb" "test/unit/subject_test.rb" 
"test/unit/subscription_mailer_test.rb" "test/unit/subscription_test.rb" 
"test/unit/user_test.rb" "test/unit/xml_content_test.rb"
Loaded suite D:/ruby/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader
Started
..........
Finished in 11.553 seconds.

rake test:functionals を呼び出すと、同じように包括的なテストのロードが行われ、rake がこれらのテストの実行を繋ぎ合わせてセット全体を網羅します。

DB2 Rails アプリケーションの問題判別

Rails アプリケーションの開発時には、予期しない結果やエラーが起こる可能性があります。最初にチェックすべきものは、/log ディレクトリー (この例では、d:\rails\teamroom\log) にあるログ・ファイルです。Rails は環境ごとのログとエラーを独自のログに書き込みます。開発環境の問題については development.log を、テスト環境の問題については test.log を調べてください。アプリケーション自体での Rails ログ・ファイルと問題診断に加え、DB2 のトレース・ユーティリティーとログも Rails アプリケーションの問題を診断する際に役立ちます。

CLI トレース

連載第 1 回で説明したように、ibm_db ドライバーは、IBM Driver for ODBC (Open Database Connectivity) and CLI (Call Level Interface) を使用して IBM データ・サーバーに接続します。これはつまり、IBM データ・サーバーとのすべてのやりとりは IBM Driver for ODBC and CLI を介して行われるため、CLI トレースで取り込むことができるということです。CLI トレースは Ruby アプリケーションが DB2 Driver for ODBC and CLI ドライバーに対して行ったあらゆる API 呼び出しを取り込み、すべての入力パラメーター、そしてドライバーからアプリケーションへの戻り値をログに記録します。したがって CLI トレースは、入力値と出力値、そして裏で実行されている Rails フレームワークによって生成された実際の SQL 文の詳細を知る上で貴重な情報源となります。

CLI トレースを有効にする手順はインフォメーション・センターに記載されています。

注 10: CLI トレースはディスクに書き込まれるため、アプリケーションのパフォーマンスに影響します。また CLI トレースは動的ではないため、トレース (または db2cli.ini に含まれる他の CLI 設定) を有効にするにはアプリケーションまたはそのアプリケーションが実行されている Web サーバーを再起動する必要があります。

CLI トレースは、アプリケーションによって正しい SQL 文が実行されていること、そしてデータベースから正しい値が返されていることを検証する貴重なツールです。Team Room アプリケーションではほとんどの SQL 文が Rails によって生成されるため、CLI トレースの出力を辿っていけば、図 2 に示した XML 文書をアップロードすることによって DB2 データベースに送信されたクエリーが明らかになります (図 2 を参照)。XML 文書をアップロードするには、以下のように 3 つの個別の表にデータを挿入する必要があります。

  1. まず、コンテンツのタイプ、名前、プラットフォーム、サイズ、サブジェクト、ファイルをアップロードしたユーザーの ID、そして更新時刻と作成時刻を documents 表に挿入します。

    リスト 33. CLI トレース: パート 1
    SQLExecDirect( hStmt=1:8 )
        ---> Time elapsed - +1.844000E-003 seconds
    ( pszSqlStr="INSERT INTO documents (content_type, name, platform, size, updated_at, 
    subject_id, user_id, created_at, data) VALUES('text/xml', 'CAN-Central.xml', 'Any', 
    125177, '2007-06-07 13:12:57', NULL, 100, '2007-06-07 13:12:49', NULL)", cbSqlStr=225 )
    ( StmtOut="INSERT INTO documents (content_type, name, platform, size, updated_at, 
    subject_id, user_id, created_at, data) VALUES(?, ?, ?, 125177, ?, NULL, 100, ?, NULL)" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 349 )
        sqlccsend( ) rc - 0, time elapsed - +1.991000E-003
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 188 ) - rc - 0, time elapsed - +2.471000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="text/xml" - x'746578742F786D6C', pcbValu...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="CAN-Central.xml" - x'43414E2D43656E74726...
    ( Row=1, iPar=3, fCType=SQL_C_CHAR, rgbValue="Any" - x'416E79', pcbValue=3, piIndicato...
    ( Row=1, iPar=4, fCType=SQL_C_CHAR, rgbValue="2007-06-07 13:12:57" - x'323030372D30362...
    ( Row=1, iPar=5, fCType=SQL_C_CHAR, rgbValue="2007-06-07 13:12:49" - x'323030372D30362...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 223 )
        sqlccsend( ) rc - 0, time elapsed - +2.380000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 127 ) - rc - 0, time elapsed - +1.909000E-003
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +1.504370E-001 seconds
    ...
  2. XML ファイルの名前と documents 表でそれに対応する ID、そして XML プレースホルダーを挿入します。

    リスト 34. CLI トレース: パート 2
    SQLExecDirect( hStmt=1:8 )
        ---> Time elapsed - +5.003000E-003 seconds
    ( pszSqlStr="INSERT INTO xml_contents (name, document_id, data) VALUES('CAN-Central.xml',
     101, '<ibm>@@@IBMXML@@@</ibm>')", cbSqlStr=108 )
    ( StmtOut="INSERT INTO xml_contents (name, document_id, data) VALUES(?, 101, ?)" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 261 )
        sqlccsend( ) rc - 0, time elapsed - +1.799000E-003
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 137 ) - rc - 0, time elapsed - +1.865000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="CAN-Central.xml" - x'43414E2D43656E747261...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="<ibm>@@@IBMXML@@@</ibm>" - x'3C69626D3E40...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 221 )
        sqlccsend( ) rc - 0, time elapsed - +1.000000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +1.840000E-003
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +8.456800E-002 seconds
    ...
  3. 上記に続く update 文によって実際に XML データが挿入されます。

    リスト 35. CLI トレース: パート 3
    SQLPrepare( hStmt=1:8 )
        ---> Time elapsed - +1.861000E-003 seconds
    ( pszSqlStr="UPDATE xml_contents SET (data) = (?) WHERE id = 101", cbSqlStr=51 )
    ( StmtOut="UPDATE xml_contents SET (data) = (?) WHERE id = 101" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 244 )
        sqlccsend( ) rc - 0, time elapsed - +1.000000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 120 ) - rc - 0, time elapsed - +1.923000E-003
    
    SQLPrepare( )
        <--- SQL_SUCCESS   Time elapsed - +3.831300E-002 seconds
    ...
    SQLDescribeParam( hStmt=1:8, usPar=1, psSQLType= ...
        ---> Time elapsed - +2.315000E-003 seconds
    
    SQLDescribeParam( psSQLType=SQL_XML, puiParamDef=0, psScale=0, ...
        <--- SQL_SUCCESS   Time elapsed - +2.457500E-002 seconds
    
    SQLBindParameter( hStmt=1:8, iPar=1, fParamType=SQL_PARAM_INPUT, fCType=SQL_C_BINARY, 
    fSQLType=SQL_XML, cbColDef=0, ibScale=0, rgbValue= ...
        ---> Time elapsed - +2.225000E-003 seconds
    
    SQLBindParameter( )
        <--- SQL_SUCCESS   Time elapsed - +3.743500E-002 seconds
    
    SQLExecute( hStmt=1:8 )
        ---> Time elapsed - +1.848000E-003 seconds
    E0D0A202020202020202020203C2F6974656D3E0D0A202020....., pcbValue=125177 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 61440 )
        sqlccsend( ) rc - 0, time elapsed - +2.710000E-004
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 61440 )
        sqlccsend( ) rc - 0, time elapsed - +2.120000E-004
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 2450 )
        sqlccsend( ) rc - 0, time elapsed - +1.300000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +2.345000E-003
    
    SQLExecute( )
        <--- SQL_SUCCESS   Time elapsed - +9.107600E-002 seconds
    ...
  4. 最後に、insert 文によって SUBJECTS 表にデータが適宜、設定されます。

    リスト 36. CLI トレース: パート 4
    SQLExecDirect( hStmt=1:10 )
        ---> Time elapsed - +2.166000E-003 seconds
    ( pszSqlStr="INSERT INTO subjects (name, size, tag, description) VALUES('Marketing', 1, 
    'sales', '@@@IBMTEXT@@@')", cbSqlStr=100 )
    ( StmtOut="INSERT INTO subjects (name, size, tag, description) VALUES(?, 1, ?, ?)" )
    ( Package="SYSSH200          ", Section=14 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 263 )
        sqlccsend( ) rc - 0, time elapsed - +1.920000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 154 ) - rc - 0, time elapsed - +1.823000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="Marketing" - x'4D61726B6574696E67', ...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="sales" - x'73616C6573', pcbValue=5, ...
    ( Row=1, iPar=3, fCType=SQL_C_CHAR, rgbValue="@@@IBMTEXT@@@" - x'40404049424D5445 ...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 182 )   
        sqlccsend( ) rc - 0, time elapsed - +3.160000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +2.128700E-002
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +1.243580E-001 seconds

DB2 トレース

トレース可能なすべての内部 DB2 関数呼び出しを取り込む DB2 トレースは、DB2 が関わるアプリケーションの問題を調べるには欠かせないツールです。このトレースは、アプリケーション実行時の DB2 内部アクティビティーに関する情報を提供します。DB2 トレースには効率的に情報収集をできるようにするための、いくつかの特徴があります。

  1. トレースのオン、オフは動的に切り替えることが可能です。トレースを有効または無効にするためにアプリケーションを再起動する必要はありません。したがって、正確な障害点がわかっていれば、トレースをその障害の直前に有効にして関連する情報だけを収集することができます。
  2. DB2トレース情報はメモリーまたはディスクに保管することができます。
  3. トレース・マスクを適用して特定のコンポーネントだけをトレースすることができます。

DB2 トレースを有効にする手順については、インフォメーション・センターを参照してください。

Rails アプリケーションでの DB2 の問題のトラブルシューティングには、アプリケーションが常駐するクライアントで DB2 トレースと CLI トレースの両方を収集すると役に立ちます。

db2diag.log および db2diag ツール

db2diag.log は、DB2 によって発生したエラーと一部の警告をログに記録します。この db2diag.log を調べると、アプリケーションの実行中に DB2 のエラーまたは警告がログに記録されたかどうかがわかります。ログに記録する情報の種類は、dbm cfg でデフォルトの 3 を他の値に設定することで、変更できます。

DB2 9® では db2diag 分析ツールを使って db2diag.log ファイルのフィルタリングとフォーマット設定を行うことができます。このツールでは例えば、特定のデータベースまたはタイムスタンプの値に関連するログ・エントリーを他のものからフィルタリングすることができます。db2diag ツールについての詳細は、インフォメーション・センターを参照してください。

よくある質問および注意事項

  1. ibm_db Ruby アダプターとドライバーを使うには DB2 クライアント・システムが必要ですか?

    必要です。連載「DB2 と Ruby on Rails」の第 1 回で説明したように、IBM_DB アダプター (ibm_db_adapter.rb) が直接の依存関係を持つ ibm_db Ruby ドライバーは、IBM Driver for ODBC and the CLI を使用して IBM データ・サーバーに接続します。そのため少なくとも IBM DB2 Driver for ODBC and CLI が必要になりますが、IBM_DB アダプターとサポートされるすべての IBM データ・サーバーとの接続を可能にするためには DB2 9 FP2 またはそれ以降のクライアント・パッケージ (CLI ドライバーが組み込まれています) で十分です。
  2. DB2 に対して Rails アプリケーションを実行しようとしたら、以下のエラーが表示されました。

    SQL0954C: Not enough storage is available in the application heap to process the statement.

    第 1 回で説明したように、DB2 9 での Rails アプリケーションには 1024 以上の APPLHEAPSZ が必要です。APPLHEAPSZ をチェックするには、ご使用のデータベースに接続して構成パラメーターを取得してください。
  3. DB2 Connect は、IBM Ruby Driver を使って DB2 i5 または DB2 for z/OS サーバーにアクセスしなければならないのですか?

    その通りです。DB2 クライアントが DB2 i5 または DB2 z/OS サーバーに接続するには DB2 Connect が必要になります。IBM DB2 Driver for ODBC and CLI を使用する場合には、有効なライセンス・ファイルがドライバーのインストール・パスに含まれていなければなりません。
  4. rake db:test:* コマンドを実行して開発でのテスト環境を複製しようとすると、rake aborted エラーが表示されます。

    database.yml ファイルを調べてください。テスト環境は、rails コマンドが生成するデフォルト構成に指定された database.yml の詳細に従ってセットアップされている必要があります。また、注 1 で述べた 2 つのファイルも修正してパッチを当ててください。
  5. IBM_DB アダプターの最新バージョンで修正されているはずの問題が発生します。一方で gem list --local コマンドを実行すると IBM_DB アダプターの最新バージョンがインストールされていると表示されます。

    <ruby_path>\lib\ruby\gems\1.8\gems\activerecord-1.15.3\lib\active_record\connection_adapters、または UNIX® での同様のパスに ibm_db_adapter.rb のコピーが含まれていないことを確認してください。

    GEM_HOME パス <ruby_path>\lib\ruby\gems\1.8\gems\ibm_db-<version>-mswin32\lib\active_record\connection_adapters (または UNIX での同様のパス) にインストールされた最新 IBM_DB gem に含まれる ibm_db_adapter.rb のコピーは 1 つでなければなりません。このファイルがRails 環境にロードされた唯一の IBM_DB アダプターになります。
  6. 今度の Rails 1.2.4 では、config.connection_adaptersRAILS_CONNECTION_ADAPTERS がなくなる予定です。したがって、第 1 回で説明したように Rails フレームワークの接続アダプター・リストに手動で「ibm_db」を登録する必要はなくなり、gem install ibm_db コマンドで IBM_DB アダプターをインストールすると、すぐに Rails 環境が検出してロードするようになります。

まとめ

Rails フレームワークに組み込まれたテスト・サポートは、テストを簡単に行えるようにしてくれます。Rails アプリケーションそれぞれのテスト、開発、実動環境は config/database.yml ファイルに定義されるので、このファイルを使用して、さまざまな目的に合わせて異なるデータベースをセットアップすることができます。新規 Rails プロジェクトを作成すると、Rails により自動的にテスト・インフラストラクチャーが生成され、モデルとコントローラーを作成するごとに、対応するテスト・スタブが作成されます。単体テストでは Rails モデルのテストを行い、機能テストと結合テストでは、さらに上位レベルで Rails アプリケーションが設計どおりに確実に動作するようにテストを行います。また、フィクスチャーではテスト用のデータを指定することが可能で、さらにモック・オブジェクトを使えばネットワーク接続や外部システムとのアクセスを考えることなくコア・アプリケーションのテストに専念できます。Rails にはこれらの機能が組み込まれているため、テストする際に並外れて便利なフレームワークとなります。


ダウンロード

内容ファイル名サイズ
Team room sample codeTeamroom3.zip10KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, Open source
ArticleID=294130
ArticleTitle=DB2 と Ruby on Rails: 第 3 回 DB2 と Ruby on Rails によるテスト
publish-date=06212007