Ajaxのテスト Selenium

7.Ajaxのテスト
Ajaxで動作するプログラムは、いままでの方法ではテストできません。サーバ側だけでプロセスが完了せずブラウザ側にあるJavaScriptと連動して動作しているからです。では、どのような方法でテストしたらよいでしょうか。簡単なプログラムを作成し、その方法を学びましょう。

7-1.プロジェクトの複写
プロジェクトAppli002を新しいプロジェクトTest004へコピーします。
(1) Appli002上で右クリックしてコピーを選択する

(2) プロジェクト名(N)を新しいプロジェクトTest004にする

(3) データベースの再作成
テストの場合はProject_testの環境で実施されるため、Project_developmentだけでは動作しません。
ここではProject_developmentを一旦ドロップし、再度DB環境をクリエイトしましょう。

Netbeansで[Rakeタスクを実行/デバック..]を選択
    フィルタ(F):
    パラメータ(P):
    一致するタスク(M): db:drop

Netbeansで[Rakeタスクを実行/デバック..]を選択
    フィルタ(F):
    パラメータ(P):
    一致するタスク(M): db:create:all


7-2.Ajaxを利用するための修正
(1) レイアウトファイルの作成
Ajaxを利用するため、基本的なレイアウトファイルを作成します。


/app/views/layouts/triangle.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Test004: <%= controller.action_name %></title>
<%= javascript_include_tag :defaults %>
</head>
<body><%= yield %>

</body>
</html>

(2) ビューの修正

/app/views/triangle/index.html.erb

(3) コントローラの修正


/app/controllers/triangle_controller.rb
class TriangleController < ApplicationController
def index
end

def calculate
@triangle = Triangle.new(params[:triangle])
if @triangle.valid?
x=@triangle.a.to_f
y=@triangle.b.to_f
z=@triangle.c.to_f
fg="ON"
fg="off" if x+y<=z
fg="off" if x+z<=y
fg="off" if y+z<=x

if fg=="ON" then
s=(x+y+z)/2
area=Math.sqrt(s*(s-x)*(s-y)*(s-z))
@result = "辺の長さ #{x}, #{y}, #{z} の三角形の面積は#{area}"
else
@result = "辺の長さ #{x}, #{y}, #{z} では三角形はできない"
end
render :partial => "results" # 結果はパーシャルで返す
else
render :partial => "error_messages"
end
end

end

(4) 結果の対応


/app/views/triangle/_results.html.erb

<%= "#{@result}" %>




/app/views/triangle/_error_messages.html.erb

<%= error_messages_for :triangle %>

(5) プログラムの実行
実際の動きは次のようになります。

7-3.テストパターンの作成
(1) NGのテスト(その1)
三角形が成り立たない場合のテストを考えましょう。三辺が1,2,3では成り立たないので、これをテストケースにします。
Ajaxを使用してメソッドを呼ぶときはxhrという命令を使って呼びます。まずは正常に戻ってくるか確認しましょう。


/test/functional/triangle_controller_test.rb
require 'test_helper'

class TriangleControllerTest < ActionController::TestCase
test "NG data" do
xhr :post, :calculate, :triangle=>{:a=>"1", :b=>"2", :C=>"3"}
assert_response :success
end
end

NetBeansで[テスト]を選択し実行します。

(2) NGのテスト(その2)
次にテンプレートが正しく利用されたか確認しましょう。


/test/functional/triangle_controller_test.rb
require 'test_helper'

class TriangleControllerTest < ActionController::TestCase
test "NG data" do
xhr :post, :calculate, :triangle=>{:a=>"1", :b=>"2", :C=>"3"}
assert_response :success
assert_template "_results"
end
end

NetBeansで[テスト]を選択し実行します。

renderingはindex.htmlになっています。これはパーシャルhtmlである_results.htmlはAjaxによってindex.htmlに取り込まれ、それがレスポンスとなるからです。

(3) NGのテスト(その3)
確認するテンプレートをindex.htmlに変更します。


/test/functional/triangle_controller_test.rb
require 'test_helper'

class TriangleControllerTest < ActionController::TestCase
test "NG data" do
xhr :post, :calculate, :triangle=>{:a=>"1", :b=>"2", :C=>"3"}
assert_response :success
assert_template "index"
end
end

NetBeansで[テスト]を選択し実行します。

(4) NGのテスト(その4)
最後に内容を検証しましょう。ここでは“面積は”という字句がないことを確認します。


/test/functional/triangle_controller_test.rb
class TriangleControllerTest < ActionController::TestCase
test "NG data" do
xhr :post, :calculate, :triangle=>{:a=>"1", :b=>"2", :C=>"3"}
assert_response :success
assert_template "index"
assert_nil assigns(:result).index("面積は")
end
end

NetBeansで[テスト]を選択し実行します。

assigns(:result)がnilオブシェクトとなっています。_resultarea.html からindex.htmlへ状態が遷移してしまったためでしょうか。

(5) NGのテスト(その5)
最後的に表示するhtmlの内容で検証しましょう。ここでは“できない”という字句があることを確認します。


/test/functional/triangle_controller_test.rb
class TriangleControllerTest < ActionController::TestCase
test "NG data" do
xhr :post, :calculate, :triangle=>{:a=>"1", :b=>"2", :C=>"3"}
assert_response :success
assert_template "index"
assert_select "div#results", /できない/
end
end

NetBeansで[テスト]を選択し実行します。

<div id=results></div>の中は“”だとの判定です。確かに実行時のhtmlソースも空のままです。Ajaxを利用したプログラムは詳細なテストできないようです。

7-4.Selenium IDEのインストール
(1) Firefoxのインストール
SeleniumIDEはFirefox3.5上で動作するので、Firefox3.5をインストールします。
Firefoxの公式サイトhttp://mozilla.jp/firefox/ から「旧バ−ジョンのダウンロード」を選択し、Firefox Setup 3.5.8.exe をダウンロードします。
・タウンロードしたexeファイルを実行します。

(2) Seleniumのインストール
・同じサイトでアドオンボタンを押し、「Selenium」を検索します。
・結果画面に[firefoxへインストール]ボタンがあるのでクリックします。

7-5.Selenium IDEの起動
(1) まずFirefoxを立ち上げ、ツールから[selenium IDE(N)]を選択します。

7-6.テストパターンの自動作成
(1) SeleniumIDE画面の右上にある赤いボタンが「記録中」であることを確認します。

(2) Fairfox画面に戻って、想定しているテストパターン1を実行します。
入力したデータがすべて記録されていくのがここから分かります。

(3) 動作の検証を組み込みます。
Firefox画面で「三角形はできない」を選択し右クリックします。その中から[verifyTextPresent 三角形はできない]を選びます。
これで、1,2,3というデータを入力すると、”三角形はできない”と表示されることを確認するコマンドとなります。

(4) 記録されたコマンドの実行
SeleniumIDE画面の[テストスイート全体を実行]ボタンをクリックします。
コマンドが実行され、Firefox画面も変化していくのが分かります。

(5) Ajaxでの問題点の解消
記録されたコマンドを繰り返し実行できることが分かったが、問題点もでてきました。検証がエラーとなっています。正しければ緑色の背景色がつくはずです。

これはAjaxを利用した場合、Webページ全体のリロードが発生しないため検証タイミングなく、非同期に検証コマンドを実行してしまい、“三角形ができない”という結果表示の前に判断しているからです。
そこで表示されるまで待つコマンドを追加します。

(6) テストの実行
検証も正しく動作し背景色が緑になっています。

(7) テストパターンの作成

(8) テストパターンの実行

7-7.主なコマンド
Seleniumのコマンドは大きく分けてActionとCheckの二種類があります。ActionはWebアプリケーションにリクエストを行うためのもので、CheckはWebアプリケーションからのレスポンス内容を検証するためのものです。
(1) Action

コマンド意味
open [url] [ ]urlを開く
type [id] [text]input要素の内容を設定する
select [id] [lavel=text]select要素の選択肢として表示される文字列を設定する。
select [id] [value=value値]select要素のvalue値を設定する
select [id] [index=index番号]select要素の位置(0から始まる)を設定する
check [id] [ ]チェックボックスラジオボタンにチェックを付ける
uncheck [id] [ ]チェックボックスラジオボタンのチェックを外す
click [id] [ ]リンクやボタンをクリックする
submit [id] [ ]formのsubmitを行う。submitボタンがないとき使う
keyPress[id][keycode]キーボードのキーを押した設定をする
clickAndWait [id] [ ]リンクやボタンをクリックしページがリロードされるのを待つ
waitForPageToLoad [ timeout ] [ ]ページがリロードされるのを待つ
waitForTextPresent [text] [ ]設定した文字列が表示されるまで待つ

・ url の書式

url (例)/triangle/ (相対パス

(例)http://127.0.0.1:3000/triangle/絶対パス

・ id の書式
identifier=id属性値またはname属性値 (例/デフォルト)identifier=commit

(または) commit
id=id属性値 (例)id=triangle_a
name=name属性値 (例)name=commit
xpath=xpath表現 (例)xpath=//div[@id=”results”]

(または) //div[@id=”results”]
link=リンクの文字列 (例)link=戻る

・ textの書式

ワイルドカード使用の文字列(* と ?) (例/デフォルト)glob:面積は*
(または) 面積は*
正規表現 (例)regexp:面積は\d+\.\d+ (意味)数字.数字
文字そのもの (例)exact:面積は6.0

・ select の書式


<select name="city">
<option value="1">東京
<option value="2">大阪
<option value="3">名古屋
</select>
label=text (例/デフォルト)label=東京 (または) 東京 (東京を選択)
value=value(例)value=2 (大阪を選択)
index=index値(例)index=2 (名古屋を選択)

・ keycode の書式

keycode (例)a

(例)\49 (キーコード\49は1)

(例)\82 (キーコード\82はU)

・ 〜AndWait の書式
・ waitForPageToLoadの書式
(注意点)リロードしないAjaxなどはタイムアウトとなる

・ timeout の書式

timeout (例/デフォルト)無指定は30秒で指定値を超えるとエラー

(例)60 (60ミリ秒)

(2) Check

コマンド意味
verifyTitle [text] titleを確認する
verifyText [id] [text]要素の文字列を確認する
verifyValue [id] [value]フォーム要素の内容を確認する。(セレクトボックス含む)
verifyElementPresent [id] 要素が存在することを確認する
verifyElementNotPresent [id] 要素が存在しないことを確認する
verifyVisible [id] 要素が存在して見えることを確認する
verifyNotVisible [id] []要素が見えないことを確認する
verifyAttribute [id@属性名] [text]要素の属性を確認する
verifyTable [テーブルのid.行.列] [text]テーブル内のテキストを確認する
verifyForTextPresent [text]表示されているテキストがあることを確認する
verifyTextNotPresent [text]設定した文字列が表示されていないことを確認する

・ verifyText の書式


<div id=”triangle”>
<form>
辺a:<input >
辺b:<input >
辺c:<input >
</form>
</div>
<div id=”results”>
</div>
[id@属性名][text] (例)//div[@id=”triangle”] regexp:辺a

(注意点) 対象要素の中のすべてのテキストが選ばれ評価される。

ここでは”辺a: 辺b: 辺c:”

・ verifyAttribute の書式


<div id="triangle">


</div>
<div id="results">
</div>
[id@属性名][text] (例)//div@id triangle

(注意点) 最初に合致したものが選択される

・ verifyTable の書式


<table>
<tr><th>都市名</th><th>郵便番号</th><th>電話番号</th></th>
<tr><td>長野市</td><td>380-xxxx</td><td>026-xxx-xxxx</td></tr>
<tr><td>松本市</td><td>390-xxxx</td><td>0263-xx-xxxx</td></tr>
<tr><td>上田市</td><td>386-xxxx</td><td>0268-xx-xxxx</td></tr>
</table>
[テーブルのid.行.列] [text] (例)//table.1.0 長野市

(注意点)セルアドレスは行・列ともに0から始まる

<table>
<tr><th>0.0</th><th>0.1</th><th>0.2</th></th>
<tr><td>1.0</td><td>1.1</td><td>1.2</td></tr>
<tr><td>2.0</td><td>2.1</td><td>2.2</td></tr>
<tr><td>3.0</td><td>3.1</td><td>3.2</td></tr>
</table>

複数パターンのテスト | index | Actionmailer

複数パターンのテスト

6.複数パターンのテスト
機能テストではフィクスチャで初期データを定義できます。しかし、これでは「横の計算」に関わるパターンのテストはできますが、「縦の計算」のパターンのテストは一つしかできません。たとえばデータの前後関係のテストでいえば0件の場合、1件の場合(前後とも関係がない)、2件の場合(前か後のみ関係がある)、3件の場合(前も後も関係がある)と4つの状態を考える必要がありますが、このうちのどれか一つしか準備できません。

6-1.プロジェクトの生成
(1) プロジェクトTest002の生成
(2) 日本語環境の設定
(3) データベースの作成
テストをする場合はProject_testの環境で実施されるのでDBもそれに対応させる必要があります。(db:create:allの指定)
NetBeansで[Rakeタスクを実行/デバッグ..]を選択します。
    フィルタ(F):
    パラメータ(P):
    一致するタスク(M): db:create:all


実行結果
(in D:/Rails_Projects/Test002)

6-2.アプリケーションの作成
(1) scaffoldの利用
NetBeansで[生成..]を選択します。
    ジェネレータ(G): scaffold
    モデル名(N): Record
    属性ペア(フィールド型)(A): cd:string a:string b:string


実行結果
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/records
exists app/views/layouts/
exists test/functional/
exists test/unit/
create test/unit/helpers/
exists public/stylesheets/
create app/views/records/index.html.erb
create app/views/records/show.html.erb
create app/views/records/new.html.erb
create app/views/records/edit.html.erb
create app/views/layouts/records.html.erb
create public/stylesheets/scaffold.css
create app/controllers/records_controller.rb
create test/functional/records_controller_test.rb
create app/helpers/records_helper.rb
create test/unit/helpers/records_helper_test.rb
route map.resources :records
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/record.rb
create test/unit/record_test.rb
create test/fixtures/records.yml
create db/migrate
create db/migrate/20100303082714_create_records.rb

(2) マイグレーションファイルの実行
NetBeansで[データベースマイグレション]→[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Test002)
== CreateRecords: migrating ==================================================
-- create_table(:records)
-> 0.0930s
== CreateRecords: migrated (0.0930s) =========================================

6-3.機能の追加
レコードから読み込んだデータaとbを加算し、結果をListに表示するとともに、同じcdの場合はその合計も表示します。また、それぞれの計算結果がマイナスのとき、その数値は赤表示とします。

(1) indexメソッドの修正


/app/controllers/records_controller.rb
class RecordsController < ApplicationController
# GET /records
# GET /records.xml
def index
@records = Record.all
st=0
ed=@records.count-1
for i in st..ed do
stfg="ON"
edfg="ON"
unless i==st then
stfg="off" if @records[i][:cd]==@records[i-1][:cd]
end
unless i==ed then
edfg="off" if @records[i][:cd]==@records[i+1][:cd]
end
kei=0.0 if stfg=="ON"
@records[i][:c]=@records[i][:a].to_f + @records[i][:b].to_f
kei+=@records[i][:c]
@records[i][:d]=nil if edfg=="off"
@records[i][:d]=kei if edfg=="ON"

end

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @records }
end
end
  :
  :
end

(2) ビュープログラムの修正


/app/views/records/index.html.erb
<h1>Listing records</h1>

<table>
<tr>
<th>CD</th>
<th align="right">A</th>
<th align="right">B</th>
<th align="right">A+B</th>
<th align="right">Total</th>
</tr><% @records.each do |record| %>
<tr>
<td><%=h record.cd %></td>
<td align="right"><%=h record.a %></td>
<td align="right"><%=h record.b %></td>
<% if record.c < 0 then %>
<td align="right"><font color="Red"><%=h record.c %></font></td>
<% else %>
<td align="right"><%=h record.c %></td>
<% end %>
<% if record.d then %>
<% if record.d < 0 then %>
<td align="right"><font color="Red"><%=h record.d %></font></td>
<% else %>
<td align="right"><%=h record.d %></td>
<% end %>
<% else %>
<td></td>
<% end %>

<td><%= link_to 'Show', record %></td>
<td%gt;<%= link_to 'Edit', edit_record_path(record) %></td>
<td><%= link_to 'Destroy', record, :confirm => 'Are you sure?', :method => :delete %></td>
</tr><% end %>
</table>

<br /><%= link_to 'New record', new_record_path %>

(3) プログラムの実行
実際のプログラムの動きは次のようになります。

6-4.テストパターンの作成
(1) 0件の場合
テストデータをフィクスチャに登録しないことにより、0件の場合のテストをします。
indexメソッドを呼んでindex.html.rebが表示されればOKとしましょう。


/test/fixtures/records.yml
# nothing




/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
test "0 cases in records" do
get :index
assert_response :success
assert_not_nil assigns(:records)
end
  :
  :
end

(2) 1件の場合
初期データはないので、プログラムのcreateメソッドを呼んでデータを登録し、テストを行います。


/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
  :
  :
test "1 case in records" do
post :create, :record=>{:id=>1, :cd=>"aaaa", :a=>"-1", :b=>"-2"}
get :index
assert_response :success
assert_equal -3.0, assigns(:records)[0][:c]
assert_equal -3.0, assigns(:records)[0][:d]
end
  :
  :
end

(3) 2件の場合
初期データはないので、プログラムのcreateメソッドを呼んでデータを登録し、テストを行います。


/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
  :
  :
test "2 cases in records" do
post :create, :record=>{:id=>2, :cd=>"bbbb", :a=>"2", :b=>"3"}
post :create, :record=>{:id=>3, :cd=>"bbbb", :a=>"-3", :b=>"-4"}
get :index
assert_response :success
assert_equal 5.0, assigns(:records)[0][:c]
assert_nil assigns(:records)[0][:d]
assert_equal -7.0, assigns(:records)[1][:c]
assert_equal -2.0, assigns(:records)[1][:d]
end
  :
  :
end

(4) 3件の場合
初期データはないので、プログラムのcreateメソッドを呼んでデータを登録し、テストを行います。


/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
  :
  :
test "3 cases in records" do
post :create, :record=>{:id=>4, :cd=>"cccc", :a=>"4", :b=>"5"}
post :create, :record=>{:id=>5, :cd=>"cccc", :a=>"-5", :b=>"-6"}
post :create, :record=>{:id=>6, :cd=>"cccc", :a=>"6", :b=>"7"}
get :index
assert_response :success
assert_equal 9.0, assigns(:records)[0][:c]
assert_nil assigns(:records)[0][:d]
assert_equal -11.0, assigns(:records)[1][:c]
assert_nil assigns(:records)[1][:d]
assert_equal 13.0, assigns(:records)[2][:c]
assert_equal 11.0, assigns(:records)[2][:d]
end
  :
  :
end

(5) マイナス表示のテスト
計算結果がマイナスのとき結果数値を赤表示しているかの検証はhtmlの中に<font color=”Red”>が埋め込まれていることを確認することで行います。


/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
  :
  :
test "minus in result" do
post :create, :record=>{:id=>7, :cd=>"dddd", :a=>"-7", :b=>"-8"}
get :index
assert_template :index
assert_select "font", 2
end
  :
  :
end

(6) プラス表示のテスト
計算結果がプラスのときは、結果数値は赤表示しません。検証はhtmlの中に<font color=”Red”>が埋め込まれていないことを確認することで行います。


/test/functional/records_controller_test.rb
class RecordsControllerTest < ActionController::TestCase
  :
  :
test "plus in result" do
post :create, :record=>{:id=>8, :cd=>"eeee", :a=>"8", :b=>"9"}
get :index
assert_template :index
assert_select "font", false
end
end

6-5.テストの実行
NetBeansで[テスト]を選択します。


実行結果
Test-unit gem not found, falling back to default test-unit
(in D:/Rails_Projects/Test002)
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/unit/helpers/records_helper_test.rb" "test/unit/record_test.rb"
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/functional/records_controller_test.rb"
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"

1.406 秒で完了しました。
7 個のテスト、0 個の失敗、0 個のエラー

functionalテスト全てのケースで成功となりました。

テストで使うメソッド | index | Ajaxのテスト

テストで使うメソッド

3.テストで使うメソッド
3-1.基本


assert(boolean, message=nil)

・booleanがtrueと評価されればテストは成功。
・booleanがfalseまたはnilの場合にテストは失敗。
・テストが失敗するとmessageが表示される。
・messageは省略できる。
・1つのテストメソッドにassert(またはassert_xxxx)メソッドが複数ある場合は、最初に失敗したassertでそのテストメソッドは中断される。
・テストメソッドが分かれていれば、失敗しても次のメソッドが実行されるので、連続して表示される。

3-2.テストメソッド一覧
(1) assert_difference


assert_difference(expressions, difference=1, message=nil) {ブロック}
・expressionsは数値を返すRubyコードを文字列で指定する。
・ブロックの最初と最後で、expressionsの式の結果に、differenceの差異が発生すれば成功。
・differenceのデフォルトは1。
(使用例)

assert_difference('Record.count', -1) do # Record件数が1件減ると成功
delete :destroy, :id => records(:one).to_param # destroyメソッドを実行
end

(2) assert_equal


assert_equal(expected, actual, message=nil)
・expectedとactualが等しい時に成功する。
・expected は期待する値。
・actual はテスト対象の値。
(使用例)

records=Record.find(:all) # Recordの全件読み込み
assert_equal 3, records.length, “件数が正しくありません” # recordsに3件データがあれば成功

(3) assert_in_delta


assert_in_delta(expected_float, actual_float, delta, message=nil)
浮動小数点には誤差があるため、assert_equalの代わりに利用する。
・expected_floatとactual_floatの差異がdelta以内なら成功する。

(4) assert_match


assert_match(pattern, string, message=nil)
・stringが正規表現patternにマッチすれば成功。

(5) assert_nil


assert_nil(object, message="")
・objectがnilであるなら成功。

(5)−2 assert_not_nil


assert_not_nil(object, message="")
・objectがnilでないなら成功。
(使用例)

assert_not_nil assigns(:records) # @recordsがnilでなければ成功

ここでassignsはインスタンス変数を参照するメソッドで、単に@recordsと書けないことに注意すること。

(6) assert_raise


assert_raise(*expected_exception_klass, message="") {ブロック}
・「*expected_exception_klass」は「予想した例外」クラス。カンマで区切って複数指定できる。
・ブロックを処理して「予想した例外」が、どれか一つ発生すれば成功。
(使用例)

assert_raise RuntimeError, ArgumentError, "例外が発生しません" do
raise NoMethodError
end

(6)−2 assert_nothing_raised


assert_nothing_raised(*expected_exception_klass, message="") {ブロック}
・「*expected_exception_klass」は「予想した例外」クラス。カンマで区切って複数指定できる。
・ブロックを処理して「予想した例外」が、一つも発生しなければ成功。
・「予想外の例外」が発生して、テストに成功した場合、Errorがカウントされる。

(7) assert_redirected_to


assert_redirected_to(options={}, message=nil)
・optionsが指定するURLオプションと、最後のアクションで呼び出されたリダイレクトのURLオプションが一致すれば成功。
(使用例)

assert_redurected_to {:controller=>”records”, :action=>”index”}

(8) assert_response


assert_response(type, message=nil)
・typeが指定するコードと、レスポンスのHTTPステータスコードが一致すれば成功。
・typeには以下のシンボルも利用可能。
:success ステータスコード200
:redirect ステータスコード300から399
:missing ステータスコード404
:error ステータスコード500から599
(使用例)

assert_response :success # HTTPステータスが200なら成功

(9) assert_select


assert_select(selector, equality, message=nil)
・selectorでCSSセレクタを記述する。
・equalityとそのCSSセレクタの要素が一致すれば成功。
(使用例)

assert_select “form”, true # formエレメントが1つ以上存在なら成功
assert_select “form input”, 4 # inputエレメントが4個なら成功
assert_select “form input”, (1..3) # inputエレメントが1〜3個なら成功

(10) assert_template


assert_template(expected, message=nil)
・expectedが指定するビューテンプレートを利用して描画されたら成功。
(使用例)

assert_template 'new'

(11) assert_valid


assert_valid(ActiveRecord_object)
ActiveRecord_objectの検証を実行して、エラーが無ければ成功。



4.テストの支援
4-1.フィクスチャ fixtures
フィクスチャとは、一般的には、テストに使う初期データのことをいいます。Railsでは二つの方法があり、一つは静的な設定で、もう一つは動的な設定です。ただし動的な設定はUNITテストの場合のみ有効です。

(1) 静的な設定
それぞれのモデルに対してテスト実行前に初期データを設定することで/test/fixtures/にYAML形式で記述します。


/test/fixtures/records.yml
one:
id: 1
a: 3
b: 5

two:
id: 2
a: 10.5
b: 9.5

(2) 動的な設定
テストの直前にデータを用意しテストの直後にデータを戻すようにsetupとteardownメソッドが用意されています。


/test/unit/record_test.rb
require 'test_helper'

class RecordTest < ActiveSupport::TestCase
def setup # このクラスのテスト直前に実行
record=Record.new(:id=>4, :a=>"1010", :b=>"101")
record.save
end

def teardown # このクラスのテスト直後に実行
record=Record.find(4)
record.destroy
end

test "data_len" do
records=Record.find(:all)
assert_equal 4, records.length # 1件追加されて全部で4件なら成功
end
end

4-2.リクエス
コントローラやビューをテストする方法を機能テストといい、その機能を呼び出すメソッドをリクエストといいいます。機能テストはこのリクエストに対するレスポンスを検証することによって行われます。
(1) GET
GETリクエストは次のメソッドを呼び出すことができます。http以下はWeb上でのURLです。
indexメソッド http://xx.xx.xx:3000/records/
newメソッド http://xx.xx.xx:3000/records/new
showメソッド http://xx.xx.xx:3000/records/1
editメソッド http://xx.xx.xx:3000/records/1/edit
xxxxメソッド http://xx.xx.xx:3000/records/xxxx

(2) POST
POSTリクエストは次のメソッドを呼び出すことができます。
createメソッド http://xx.xx.xx:3000/records/?a=”1.0”&b=”2.0”

(3) PUT
PUTリクエストは次のメソッドを呼び出すことができます。
updateメソッド http://xx.xx.xx:3000/records/1?_method=”put”&a=”1.0”&b=”2.0”

(4) DELETE
DELETEリクエストは次のメソッドを呼び出すことができます。
destoryメソッド http://xx.xx.xx:3000/records/1&_method=”delete”



5.テストモジュールのまとめ
5-1.単体テスト UNIT test
プログラム内部で行うテストで、対象とするメソッドの下位モジュールがないときはスタブを作成してテストを行います。

5-2.機能テスト FUNCTIONAL test
プログラム外部から行うテストで、あたかもブラウザに向かって操作する感覚でテスト仕様を作成し検証します。



テストの手順 | index | 複数パターンのテスト

テストの手順

[8]テスト
1.テスト駆動開発
RubyおよびRuby on Railsの特徴の一つにテスト駆動の開発があります。これは最初にテストコードを書いてからソースコードを書いてテストをしていく方法です。「仕様に基づいて動作すると結果はこうなるはずだ」というテストコードを書くことによるメリットは、一度書いておけば繰り返し使えるということ挙げられます。大きなシステムの仕様であればなおさらで、特に仕様変更に対しソースコードを書き直すことによりデグレートを起こしてしまう危険性の検知に多いに役立ちます。



2.テストの手順
2-1.プログラムの仕様
レコードにあるaとbを読み込んでa+bを計算し、結果を表示するというプログラムにしましょう。

2-2.プロジェクトの生成
(1) プロジェクトTest000の生成
(2) 日本語環境の設定
(3) データベースの作成
テストをする場合はProject_testの環境で実施されるのでDBもそれに対応する必要があります。(db:create:allの指定)
NetBeansで[Rakeタスクを実行/デバッグ..]を選択します。
    フィルタ(F):
    パラメータ(P):
    一致するタスク(M): db:create:all


実行結果
(in D:/Rails_Projects/Test000)

2-3. アプリケーションの作成
(1) scaffoldの利用
NetBeansで[生成..]を選択
    ジェネレータ(G): scaffold
    モデル名(N): Record
    属性ペア(フィールド型)(A): a:string b:string


実行結果
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/records
exists app/views/layouts/
exists test/functional/
exists test/unit/
create test/unit/helpers/
exists public/stylesheets/
create app/views/records/index.html.erb
create app/views/records/show.html.erb
create app/views/records/new.html.erb
create app/views/records/edit.html.erb
create app/views/layouts/records.html.erb
create public/stylesheets/scaffold.css
create app/controllers/records_controller.rb
create test/functional/records_controller_test.rb
create app/helpers/records_helper.rb
create test/unit/helpers/records_helper_test.rb
route map.resources :records
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/record.rb
create test/unit/record_test.rb
create test/fixtures/records.yml
create db/migrate
create db/migrate/20100225051438_create_records.rb

(2) マイグレーションファイルの実行
NetBeansで[データベースマイグレション]→[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Test000)
== CreateRecords: migrating ==================================================
-- create_table(:records)
-> 0.1100s
== CreateRecords: migrated (0.1100s) =========================================

(4) 機能の追加
レコードから読み込んだデータaとbを加算し、結果もListに表示させるようにします。


・ indexメソッドの修正


/app/controllers/records_controller.rb
class RecordsController < ApplicationController
# GET /records
# GET /records.xml
def index
@records = Record.all
@records.each do |record|
record[:c] = record.a + record.b
end

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @records }
end
end
  :
  :
end



・ビュープログラムの修正


/app/views/records/index.html.erb
<h1>Listing records</h1>

<table>
<tr>
<th>A</th>
<th>B</th>
<th>A+B</th>
</tr><% @records.each do |record| %>
<tr>
<td><%=h record.a %></td>
<td><%=h record.b %></td>
<td><%=h record.c %></td>
<td><%= link_to 'Show', record %></td>
<td><%= link_to 'Edit', edit_record_path(record) %></td>
<td><%= link_to 'Destroy', record, :confirm => 'Are you sure?', :method => :delete %></td>
</tr><% end %>
</table>

<br /><%= link_to 'New record', new_record_path %>

(5) 生成されたテストプログラムの実行
まずRuby on Railsで生成されたテストプログラムを実行してみましょう。
NetBeansで[テスト]を選択します。

(6) テスト結果の表示
テスト結果がビジュアルに表示されます。ここでは8件のテストを実行しすべて成功となっています。

2-4.テストパターンの作成
(1) テストデータの登録


/test/fixtures/records.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

one: # 本来はユニークであれば何でもいいが
# scaffoldがテストパターンの記述の中で
# oneとtwo使用しているため変更できない。
id: 1
a: 3
b: 5

two:
id: 2
a: 10.5
b: 9.5

data3:
id: 3
a: -5
b: -50.5

上記の記述によって3つのデータが、テストのときrecordsテーブルに登録され実行されます。

(2) 単体テストの登録


/test/unit/record_test.rb
require 'test_helper'

class RecordTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end

test "data_len" do
records=Record.find(:all)
assert_equal 3, records.length, "件数が正しくありません" # recordsに3件データがあれば成功
end
end




test テストの名前 do

end

これで一つのテストパターンとなり、このブロックの中にRuby言語でテスト内容を記述します。

(3) 機能テストの登録
機能テストはクライアント側に立ってプログラムを実行した結果を評価するという感覚で行います。


/test/functional/records_controller_test.rb
require 'test_helper'

class RecordsControllerTest < ActionController::TestCase
test "should get index" do
get :index # indexメソッドを実行
assert_response :success # HTTPステータスが200なら成功
assert_not_nil assigns(:records) # @recordsがnilでないなら成功
end

test "should get new" do
get :new # newメソッドを実行
assert_response :success # HTTPステータスが200なら成功
end

test "should create record" do
assert_difference('Record.count') do # Record件数が1件増えると成功
post :create, :record => { } # createメソッドを実行
end

assert_redirected_to record_path(assigns(:record)) # リダイレクト先の検証
end

test "should show record" do
get :show, :id => records(:one).to_param # showメソッドを実行
assert_response :success # HTTPステータスが200なら成功
end

test "should get edit" do
get :edit, :id => records(:one).to_param # editメソッドを実行
assert_response :success # HTTPステータスが200なら成功
end

test "should update record" do
put :update, :id => records(:one).to_param, :record => { } # updateメソッドを実行
assert_redirected_to record_path(assigns(:record)) # リダイレクト先の検証
end

test "should destroy record" do
assert_difference('Record.count', -1) do # Record件数が1件減ると成功
delete :destroy, :id => records(:one).to_param # destroyメソッドを実行
end

assert_redirected_to records_path # リダイレクト先の検証
end

test "access index" do
get :index # indexメソッドを実行
assigns(:records).each do |r| # メソッド実行後の@recordsを取得
assert_equal 8.0, r[:c] if r[:id] == 1 # :idが1のとき8.0なら成功
assert_equal 20.0, r[:c] if r[:id] == 2 # :idが2のとき20.0なら成功
assert_equal -55.5, r[:c] if r[:id] == 3 # :idが3のとき-55.5なら成功
end
end

end

赤の部分はindexメソッドにある演算が正しく行われているかをテストしています。

・get、post、put、deleteの意味
ここでメソッドを呼ぶときget、post、put、deleteと異なっている理由は、それぞれのメソッドが要求しているインターフェイスの違いによります。詳細は開発編(1)を参照してください。

2-5.テストの実行
NetBeansで[テスト]を選択します。


実行結果
Test-unit gem not found, falling back to default test-unit
(in D:/Rails_Projects/Test000)
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/unit/helpers/records_helper_test.rb" "test/unit/record_test.rb"
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/functional/records_controller_test.rb"

1) 失敗:
test_access_index(RecordsControllerTest):<8.0> expected but was <"35">.
/test/functional/records_controller_test.rb:49:in `test_access_index'
/test/functional/records_controller_test.rb:48:in `each'
/test/functional/records_controller_test.rb:48:in `test_access_index'
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"

1.453 秒で完了しました。
10 個のテスト、1 個の失敗、0 個のエラー

10個のテスト中1個失敗しています。
テスト結果の詳細をみると3 + 5 は 8.0 になることを予想しているのに “35”になっていることが分かります。
文字列の足し算(連結)をしているようです。

2-6.プログラムの修正
・indexメソッドの修正


/app/controllers/records_controller.rb
class RecordsController < ApplicationController
# GET /records
# GET /records.xml
def index
@records = Record.all
@records.each do |record|
record[:c] = record.a.to_f + record.b.to_f
end

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @records }
end
end
  :
  :
end

2-7.テストの実行
NetBeansで[テスト]を選択します。


実行結果
Test-unit gem not found, falling back to default test-unit
(in D:/Rails_Projects/Test000)
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/unit/helpers/records_helper_test.rb" "test/unit/record_test.rb"
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
"test/functional/records_controller_test.rb"
C:/Ruby/bin/ruby.exe -I"lib;test" -r"C:/Program Files/NetBeans 6.8/ruby2/nb_test_runner.rb"
"C:/Ruby/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"

1.172 秒で完了しました。
10 個のテスト、0 個の失敗、0 個のエラー

10個のテストをして全て成功しました。



ActiveScaffold | index | テストで使うメソッド

Actionmailer

[9]Actionmailer

1.Actionmailer
メールの送信やメール本文の解析を行うActionMailerパッケージが標準装備されています。これを使ってSMTPPOP3と簡単に遣り取りすることができます。ここでは簡単なメーラを作ってみましょう。

1-1.プロジェクトの生成
(1) プロジェクトAppli010を生成する
(2) 日本語環境の設定
(3) データベースの作成

1-2.テーブルpageの作成
(1) モデルの作成
NetBeansで[生成..]を選択します
    ジェネレータ(G):model
    引数(A):Page


実行結果
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/page.rb
create test/unit/page_test.rb
create test/fixtures/pages.yml
create db/migrate
create db/migrate/20100210000356_create_pages.rb

(2) マイグレーションファイルの修正


/db/migrate/20100210000356_create_pages.rb
class CreatePages < ActiveRecord::Migration
def self.up
create_table :pages do |t|
t.column :address, :string
t.timestamps
end
end

def self.down
drop_table :pages
end
end

(3) マイグレーションファイルの実行
NetBeansで[データベースマイグレーション]→[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Appli010)
== CreatePages: migrating ====================================================
-- create_table(:pages)
-> 0.1090s
== CreatePages: migrated (0.1090s) ===========================================

1-3.テーブルemailの作成
(1) モデルの作成
NetBeansで[生成]を選択します。
    ジェネレータ(G):model
    引数(A):Email


実行結果
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/email.rb
create test/unit/email_test.rb
create test/fixtures/emails.yml
exists db/migrate
create db/migrate/20100210000929_create_emails.rb

(2) マイグレーションファイルの修正


/db/migrate/20100210000929_create_emails.rb
class CreateEmails < ActiveRecord::Migration
def self.up
create_table :emails do |t|

t.column :page_id, :integer
t.column :subject, :string
t.column :receivedate, :timestamp
t.column :messageid, :string
t.column :header, :text
t.column :body, :text

t.timestamps
end
end

def self.down
drop_table :emails
end
end

(3) マイグレーションファイルの実行
NetBeansで[データベースマイグレーション]→[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Appli010)
== CreateEmails: migrating ===================================================
-- create_table(:emails)
-> 0.1560s
== CreateEmails: migrated (0.1560s) ==========================================

1-4.テーブルattachmentの作成
(1) モデルの作成
NetBeansで[生成]を選択
    ジェネレータ(G):model
    引数(A):Attachment


実行結果
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/attachment.rb
create test/unit/attachment_test.rb
create test/fixtures/attachments.yml
exists db/migrate
create db/migrate/20100210001457_create_attachments.rb

(2) マイグレーションファイルの修正


/db/migrate/20100210001457_create_attachments.rb
class CreateAttachments < ActiveRecord::Migration
def self.up
create_table :attachments do |t|

t.column :page_id, :integer
t.column :file, :binary
t.column :filename, :string
t.column :description, :string

t.timestamps
end
end

def self.down
drop_table :attachments
end
end

(3) マイグレーションファイルの実行
NetBeansで[データベースマイグレーション]→[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Appli010)
== CreateAttachments: migrating ==============================================
-- create_table(:attachments)
-> 0.1100s
== CreateAttachments: migrated (0.1100s) =====================================

1-5.リレーションの宣言


/app/models/page.rb
class Page < ActiveRecord::Base
has_many :emails
has_many :attachments
end

1-6.メール受信専用モデルの作成
(1) モデルの生成
NetBeansで[生成..]を選択します。
    ジェネレータ(G):mailer
    引数(A):Mailreceiver


実行結果
exists app/models/
create app/views/mailreceiver
exists test/unit/
create test/fixtures/mailreceiver
create app/models/mailreceiver.rb
create test/unit/mailreceiver_test.rb

(2) モデルの編集


/app/models/mailreceiver.rb

require 'net/pop'

class Mailreceiver < ActionMailer::Base

@address = 'POP3saver' # <= 自分のPOP3サーバー
@port = 110 # <= ポート番号 110など
@id = 'userid' # <= ユーザーID
@pass = 'password' # <= パスワード

def receive(email)
page = Page.find_by_address(email.from.first) ||
Page.create(:address => email.from.first)
str = email.subject.kconv(Kconv::UTF8, Kconv::JIS) # JIS から UTF8 に変換
page.emails.create(
:subject => str,
:body => email.body,
:header => email.header,
:messageid => email.message_id,
:receivedate => email.date
)

if email.has_attachments?
for attachment in email.attachments
str = attachment.original_filename.kconv(Kconv::UTF8, Kconv::JIS)
page.attachments.create({
:file => attachment,
:description => email.subject,
:filename => str
})
end
end
end

def Mailreceiver.r_m
pop = Net::POP3.new(@address, @port)
pop.start(@id, @pass)
if !pop.mails.empty? then
pop.each_mail do |m|
receive(m.pop) # 受信したメールを、「処理」する(DBへ登録)
m.delete # 受信したメールをサーバから削除
end
end
pop.finish
end
end

1-7.動作確認


/app/models/mailreceiver.rb
D:\Rails_Projects\Appli010>ruby script/runner Mailreceiver.r_m
D:\Rails_Projects\Appli010>

1-8.メール送信サーバの設定


/config/environments/development.rb

# Settings specified here will take precedence over those in config/environment.rb

# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes = false

# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true

# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_view.debug_rjs = true
config.action_controller.perform_caching = false

# Don't care if the mailer can't send

config.action_mailer.raise_delivery_errors = true

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => 'SMTPsever', # <= 自分のSMTPサーバー
:port => 25, # <= ポート番号 25,587など
:authentication => :login, # <= 認証方法 :plain, :login, :cram_md5など
:user_name => "username", # <= ユーザーID
:password => "password", # <= パスワード
:domain => "domain" # <= ドメイン
}

1-9.メール送信専用モデルの作成
(1) モデルの生成
NetBeansで[生成]を選択します。
    ジェネレータ(G):mailer
    引数(A):Mailsender


実行結果
exists app/models/
create app/views/mailsender
exists test/unit/
create test/fixtures/mailsender
create app/models/mailsender.rb
create test/unit/mailsender_test.rb

(2) モデルの修正


/app/models/mailsender.rb
class Mailsender < ActionMailer::Base
@@default_charset = 'iso-2022-jp'

def okuru(okuru)
str = okuru[:subject].kconv(Kconv::JIS, Kconv::UTF8) # UTF8 から JIS に変換
@subject = str
@recipients = okuru[:to]
@from = okuru[:from]
@sent_on = Time.now
str = okuru[:body].kconv(Kconv::JIS, Kconv::UTF8) # UTF8 から JIS に変換
@body = str
@headers["reply-to"] = okuru[:from]
end

end

1-10.コントローラrmailerの作成
(1) コントローラの生成
NetBeansで生成を選択
    ジェネレート(G):controller
    名前(N):rmailer
    ビュー(V):list show new okuru ukeru


実行結果
exists app/controllers/
exists app/helpers/
create app/views/rmailer
exists test/functional/
create app/controllers/rmailer_controller.rb
create test/functional/rmailer_controller_test.rb
create app/helpers/rmailer_helper.rb
create app/views/rmailer/list.html.erb
create app/views/rmailer/show.html.erb
create app/views/rmailer/new.html.erb
create app/views/rmailer/okuru.html.erb
create app/views/rmailer/ukeru.html.erb

(2) コントローラの編集


/app/controllers/rmailler_controller.rb
class RmailerController < ApplicationController
layout "rmailer"

def list
@emails = Email.find(:all)
end

def show
@email = Email.find(params[:id])
end

def new
end

def okuru
@okuru = params[:okuru]
logger.info "EMAIL:" + params[:okuru][:to]
Mailsender.deliver_okuru(@okuru)
end

def ukeru
job = open('|ruby script/runner Mailreceiver.r_m')
end

end

1-11.ビューの作成
(1) 送信用メールフォームの作成


/app/views/rmailer/new.html.erb
<br>
<div class="box"><%= link_to "受信箱", :action => "list" %>
</div>
<br>
<h1>新規送信メール</h1><% form_for(:okuru, :url =>{:action => "okuru"}) do |f| %><%= error_messages_for('okuru') %>
<table width="60%">
<tr><th>subject</th><td><%= f.text_field(:subject, :size => 60 ) %></td></tr>
<tr><th>to</th><td><%= f.text_field(:to, :size => 60 ) %></td></tr>
<tr><th>from</th><td><%= f.text_field(:from, :size => 60 ) %></td></tr>
<tr><th>body</th><td><%= f.text_area(:body, :cols => 50, :rows => 10 ) %></td></tr>
<tr><th></th><td><%= submit_tag("送信") %>
</table><% end %>

(2) 送信完了通知の作成


/app/views/rmailer/okuru.html.erb
<br>
<div class="box"><%= link_to "受信箱", :action => "list" %>
</div>
<br>
<P>送信終了しました</p>

(3) 受信完了通知の作成


/app/views/rmailer/ukeru.html.erb
<br>
<div class="box"><%= link_to "受信箱", :action => "list" %>
</div>
<br>
<P>受信終了しました</p>

(4) 受信箱の作成


/app/views/rmailer/list.html.erb
<br>
<div class="box"><%= link_to "新規送信", :action => "new" %><%= link_to "受信", :action => "ukeru" %>
</div>
<br>
<h1>受信箱</h1>
<table>
<thead>
<tr>
<th>subject</th>
<th>address</th>
<th>receivedate</th>
<th></th>
</tr>
</thead>

<tbody><%-
fg = "ON"
@emails.each do |e|
@page = Page.find(e.page_id)
if fg=="ON" then
fg = "off"
-%>
<tr><%-
else
fg = "ON"
-%>
<tr class="odd"><%- end -%>
<td><%=h e.subject %></td>
<td><%=h @page.address %></d>
<td><%=h e.receivedate %></td>
<td><%= link_to "show", :action => "show", :id => e.id %></td>
</tr><% end %>
</tbody>
</table>

(5) 受信メール表示の作成


/app/views/rmailer/show.html.erb
<br>
<div class="box"><%= link_to "受信箱", :action => "list" %>
</div>
<br>
<h1>受信メール</h1><% @page = Page.find(@email.page_id) %>
<table width="60%">
<tr><th>subject</th><td><%=h @email.subject %></td></tr>
<tr><th>address</th><td><%=h @page.address %></td></tr>
<tr><th>receivedate</th><td><%=h @email.receivedate %></td></tr>
<tr><th>body</th><td><%=h @email.body %></td></tr>
</table>

1-12.スタイルシートの作成
(1) CSSで使用する画像の準備
/public/images/に透過のtable-back.gifを用意します。
これは見出し部分を立体的に見せる工夫で背景色より薄めの色で枠線を作ります。

(2) IE対応
IE6.0ではtr:hoverが動作しません。
http://www.xs4all.nl/~peterned/csshover.htmlからcsshover.htcをダウンロードし、
ダウンロードしたファイルをcsshover3.htcとリネームして/public/stylesheets/に保存します。

(3) CSSファイルの作成


/public/stylesheets/rmailer.css
/* 見出しを作る */
h1 {
padding: 10px;
border: medium solid #5C443A;
color: #5C443A;
font-family: sans-serif;
}


/* 文字に背景色を付ける */
.box a {
text-decoration: none; /* 下線を消す */
background-color: #cdcdcd; /* 背景色を薄い灰色 */
color: #000000; /* 文字を黒色 */
padding: 4px 10px 2px 10px;
}
.box a:link { /* 未訪問のリンク */
background-color: #cdcdcd; /* 背景色を薄い灰色 */
color: #000000; /* 文字を黒色 */
}
.box a:visited { /* 訪問済みのリンク */
background-color: #cdcdcd; /* 背景色を薄い灰色 */
color: #000000; /* 文字を黒色 */
}
.box a:hover { /* マウスがのっているとき */
background-color: #808080; /* 背景色を濃い灰色 */
color: #ffffff; /* 文字を白色 */
}
.box a:active { /* クリック時のリンク */
background-color: #808080; /* 背景色を濃い灰色 */
color: #ffff00; /* 文字を黄色 */
}

/* テーブル行に背景色を付ける */
body {
behavior: url(/stylesheets/csshover3.htc);
}

table {
border-top:1px solid #5C443A;
border-left:1px solid #5C443A;
border-collapse:collapse;
background:#ffffff;
font-size:90%;
}
caption {
color:#5C443A;
font-weight:bold;
text-align:center;
text-transform: uppercase;
}
thead th, tfoot th, tfoot td {
border-right:1px solid #5C443A;
border-bottom:1px solid #5C443A;
background:#5C443A;
color:#ffffff;
background-image:url(/images/table-back.gif);
background-position:left top;
padding:3px 10px 3px 10px;
text-align:center;
}
td, th {
padding:4px;
}
tbody tr th, tbody tr td {
border-right:1px solid #5C443A;
border-bottom:1px solid #5C443A;
}
tbody tr:hover td, tbody tr:hover th {
background: #996633;
color: #ffffff;
}
tbody tr:hover td a, tbody tr:hover th a {
background: #996633;
color: #ffffff;
}
tbody td a, tbody th a {
border: none;
background: transparent;
color: #000000;
text-decoration: none;
}
tbody td a:hover, tbody th a:hover {
background: #996633;
color: #ffffff;
text-decoration: underline;
}
.odd {
background: #ffeebb;
}

1-13.レイアウトファイルの作成


/app/views/layouts/rmailer.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8"/>
<title>Rmailer</title>
<%= stylesheet_link_tag 'rmailer' %>
</head>
<body><%= yield %>

</body>
</html>

1-14.動作確認
(1) ブラウザよりhttp://127.0.0.1:3000/rmailer/listによる起動

(2) 受信メール表示

(3) 新規送信



Ajaxのテスト | index | ActiveResource

ActiveScaffold

[7]ActiveScaffold
1.ActiveScaffold
いままで内蔵しているScaffoldを使用してCRUDを構築してきましたが、さらに機能アップされた、また見栄えのよいActiveScaffoldを取り上げます。

1-1. プロジェクトの生成
(1) プロジェクトAppli008の生成
(2) 日本語環境の設定
(3) データベースの作成

1-2. 住所録テーブルAddressの作成
(1) モデルの生成
NetBeansで[生成]を選択
    ジェネレート(G):model
    引数(A):Address (モデル名の先頭文字は大文字とすること)


実行結果
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/address.rb
create test/unit/address_test.rb
create test/fixtures/addresses.yml
create db/migrate
create db/migrate/20100204041928_create_addresses.rb

(2) マイグレーションファイルの修正


/db/migrate/yyyymmddhhmmss_create_addresses.rb
class CreateAddresses < ActiveRecord::Migration
def self.up
create_table :addresses do |t|
t.string :namae
t.string :yubin, :limit=>8
t.string :jusho
t.string :denwa, :limit=>20
t.timestamps
end
end

def self.down
drop_table :addresses
end
end

(3) マイグレーションの実行
NetBeansで[データベースマイグレーション]より[現在のバージョンへ]を選択します。


実行結果
(in D:/Rails_Projects/Appli008)
== CreateAddresses: migrating ================================================
-- create_table(:addresses)
-> 0.1090s
== CreateAddresses: migrated (0.1090s) =======================================

1-3. ActiveScaffoldのインストール
(1) github.comからのダウンロード
IE以外のブラウザを使いhttp://activescaffold.com/でメインのホームページを呼び出し
をクリックする

(2) githubの画面となるので、をクリックする

(3) zip形式のactivescaffold-active_scaffold-1acd1ab.zipを、適当な場所にダウンロードする

(4) ダウンロードしたファイルを適当な場所に解凍する

(5) 解凍したファイルを/vender/plugins/フォルダにコピーしactive_scaffoldと名前を変更する

1-4.リソースファイルの生成
(1) 日本語のリソースファイルの生成


コマンド プロンプト
D:\Rails_Projects\Appli008>ruby script/generate i18n ja
debug updating environment.rb ...
debug fetching ja.yml fro rails-i18n repository...
exists config/locales
update config/environment.rb
create config/locales/ja.yml
debug 1 models found.
debug 0 translat on keys found in v ews.
debug translating activerecord.models.address ...
debug translating activerecord.attributes.address.namae ...
debug translating activerecord.attributes.address.yubin ...
debug translating activerecord.attributes.address.jusho ...
debug translating activerecord.attributes.address.denwa ...
failed to translate “address” into “ja” language.
debug took 1.438 secs to translate.
create config/locales/translation_ja.yml

(2) translation_ja.ymlの更新
生成された字句を修正し、モデルに関する日本語化を完成させます。


/config/locals/translation_ja.yml
ja:
activerecord:
models:
address: "住所録"

attributes:
address:
namae: "名前"
yubin: "郵便番号"
jusho: "住所"
denwa: "電話"

(3) layoutファイルの作成


/app/views/layouts/activescaffold.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8"/>
<title>Appli008</title>
<%= javascript_include_tag :defaults %>
<%= active_scaffold_includes %>
</head>
<body><%= yield %>

</body>
</html>

(4) addressesコントローラおよびヘルパーの作成
NetBeansで[生成..]を選択する
    ジェネレータ(G): controller
    名前(N): addresses


実行結果
exists app/controllers/
exists app/helpers/
create app/views/addresses
exists test/functional/
create test/unit/helpers/
create app/controllers/addresses_controller.rb
create test/functional/addresses_controller_test.rb
create app/helpers/addresses_helper.rb
create test/unit/helpers/addresses_helper_test.rb

(5) addressesコントローラの修正


/app/controllers/addresses_controller.rb
class AddressesController < ApplicationController
layout 'activescaffold'

active_scaffold :address do |config|
config.columns = [:namae, :yubin, :jusho, :denwa, :created_at]
config.show.columns = [:id, :namae, :yubin, :jusho, :denwa, :created_at, :updated_at]
config.list.per_page = 10
config.list.sorting = [:yubin => :ASC]
end

end

ActiveScaffoldは、controller.rbでさまざまな条件を設定します。
config.actions.exclude不要なアクションを設定

指定できるアクションは:list一覧、:show参照、:update修正、

:delete削除、:create新規追加、:search検索

(使用例)config.actions.exclude :update, :delete
config.columnsすべてのアクションに対し表示するカラムを設定
config.show.columnsShowアクションに対し表示するカラムを設定
config.list.per_pageListアクションのときの表示行数を設定
config.list.sorting Listアクションのときのデフォルトの表示順序を設定

(6) addressesヘルパーの修正


/app/helpers/addresses_helper.rb
module AddressesHelper
def created_at_column(record)
record[:created_at].strftime("%Y-%m-%d")
end
def updated_at_column(record)
record[:updated_at].strftime("%Y-%m-%d")
end

def created_at_form_column(record, name)
input(:record, :created_at, :name=>name, :order=>[:year,:month,:day], :use_month_numbers=>true)
end
def updated_at_form_column(record, name)
input(:record, :updated_at, :name=>name, :order=>[:year,:month,:day], :use_month_numbers=>true)
end
end

表示内容を変更する場合は、ヘルパーにメソッドを追加します。ここでは日付をyyyymmdd形式に変更しています。

(7) 動作確認
・一覧表示 List

・新規作成 Create

・表示 Show

(8) 日本語化されていない項目の追加
idやcreated_atなどモデルの日本語化で定義していなかった項目を追加します。


/config/locals/translation_ja.yml
ja:
activerecord:
models:
address: "住所録"

attributes:
address:
id: "ID"
namae: "名前"
yubin: "郵便番号"
jusho: "住所"
denwa: "電話"
created_at: "作成日"
updated_at: "更新日"

(9) 実行

1-5.検証機能の追加
(1) モデルにvalidationを追加します。


/app/models/address.rb
class Address < ActiveRecord::Base
validates_presence_of :namae
validates_presence_of :yubin
validates_presence_of :jusho
validates_presence_of :denwa
end

(2) 動作確認



Ajax | index | テストの手順

Ajax

[6]Ajax

1.Ajax
通常Webアプリケーションではページ単位で画面が遷移します。これに対しAjax(Asynchronous javaScript+XML)はページの一部分を書き換えることで、元の画面を保持しながら進めることができます。
ActiveFormで作成したプログラムをベースに、Ajaxを利用すると、どのように変化するか理解しましょう。

1-1.プロジェクトの複写
まず始めにプロジェクトAppli002を新しいプロジェクトAppli006へコピーします。

(1) Appli002上で右クリックしてコピーを選択する

(2) プロジェクト名(N)を新しいプロジェクトAppli006にする

1-2.画面とプログラムの流れ
(1) 通常の場合
プロジェクトAppli002は三角形の面積を求めるプログラムで、画面の遷移は次のようになっています。

(2) Ajaxを利用した場合
これに対しAjaxは、元の画面を保持しながら進めることができます。Ajaxを利用した三角形の面積を求めるプログラムを使ってその流れを説明します。

ここで_kekka.html.erbはパーシャルerbと呼ばれ、部分的に更新を行うプログラムは約束事で先頭に”_”を付けます。

1-3.プログラムの修正
(1) 入力プログラム(index.html.erbの修正)
AjaxJavascriptで実現しているので、まずjavascriptを対象とするビュープログラムに埋め込みます。
そのビュープログラム上で特定のフィールドを監視し、
そのフィールドが更新された場合Ajaxを利用してサーバとデータの送受信を行います。
またデータを受け取るフィールドを用意することで結果を指定の場所に表示できます。

/app/views/triangle/index.html.erb

ここでobserve_fieldを説明します。
このobserve_field はrailsのRJSテンプレートという機能を使っています。
RJSテンプレートとは、javascriptを書かなくても簡単にかける機能でパラメータ指定のみで利用できます。
:cこのフィールドを監視する
:update=>"kekka"この場所を更新する
:position=>"top"updateで指定した要素のどこに表示するか

“top”:先頭

”bottom”:最後

”before”:要素の直前

”after”:要素の直後

指定しないときは上書き
:url=>{:action=>"calculate"}どのメソッドを呼び出すか指定する
:frequency=>0.5監視間隔を指定する
:submit=>"param"データを送信するフォームまたは子要素を持つ親のidを指定する
(2) 面積を求めるプログラム(triangle_controller.rbの修正) このプログラムの最後で制御をパーシャルプログラムに移します。
/app/controllers/triangle_controller.rb
class TriangleController < ApplicationController def index end def calculate x=params[:a].to_f y=params[:b].to_f z=params[:c].to_f fg="ON" fg="off" if x+y<=z fg="off" if x+z<=y fg="off" if y+z<=x if fg=="ON" then s=(x+y+z)/2 area=Math.sqrt(s*(s-x)*(s-y)*(s-z)) @result = "辺の長さ #{x}, #{y}, #{z} の三角形の面積は#{area}" else @result = "辺の長さ #{x}, #{y}, #{z} では三角形はできない" end render :partial => "kekka" end end
(3) 結果表示プログラム(_kekka.html.erbの新規作成)
/app/views/triangle/_kekka.html.erb
<%= "#{@result}" %><br/>
(4) 動作確認 プログラムを実行して動作を確認します。
認証機能 | index | ActiveScaffold