RailsでRspecを使用してテストを書いているときに、viewsに関連するテストも生成されます。そのviewsでassosiationを使用しているときにはテストデータをあらかじめbeforeで宣言する必要があるのですが、少しわかりにくかったのでまとめておきます。
具体的には下記のような画面があるとします。
この画面には、銀行口座という項目があるのですが、この銀行口座は支払方法オブジェクトに関連するオブジェクトです。
[ruby]
payment_method は支払方法
class PaymentMethod < ActiveRecord::Base
bank_account は銀行口座
belongs_to :bank_account
end [/ruby]
このときに生成されるrspecのviewに関するテストは以下の通りとなります。
[ruby]
encoding:utf-8
require 'spec_helper'
describe "payment_methods/edit" do before(:each) do @payment_method = assign(:payment_method, stub_model(PaymentMethod, :payment_method_name => "MyString", :use_credit => false, :jigyonushi_kari_flg => false, :bank_account_id => 1, :create_user_id => 1, :update_user_id => 1, :team_id => 1 )) end
it "renders the edit payment_method form" do render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "form", :action => payment_methods_path(@payment_method), :method => "post" do
assert_select "input#payment_method_payment_method_name", :name => "payment_method[payment_method_name]"
assert_select "input#payment_method_use_credit", :name => "payment_method[use_credit]"
assert_select "input#payment_method_jigyonushi_kari_flg", :name => "payment_method[jigyonushi_kari_flg]"
assert_select "input#payment_method_bank_account_id", :name => "payment_method[bank_account_id]"
end
end end [/ruby]
このままだとテストを実行時に以下のエラーが表示されます。
[ruby]
ActionView::Template::Error: undefined method map' for nil:NilClass
./app/views/payment_methods/_form.html.erb:20:in
block in app_views_payment_methodsform_html_erb2737277445681912305_70127108919120'
./app/views/payment_methods/form.html.erb:1:in _app_views_payment_methods__form_html_erb__2737277445681912305_70127108919120'
./app/views/payment_methods/edit.html.erb:3:in
app_views_payment_methods_edit_html_erb__3129271270495268613_70127108132320'
./spec/views/payment_methods/edit.html.erb_spec.rb:20:in `block (2 levels) in <top (required)>'
[/ruby]
このメッセージについて最初はよくわかりませんでした。テストデータベースに該当する bank_account データが存在しないからか?と思いfixtureでデータをロードしてもやはり同じエラーが表示されます。
[ruby]
encoding:utf-8
require 'spec_helper'
describe "payment_methods/edit" do before(:each) do @payment_method = assign(:payment_method, stub_model(PaymentMethod, :payment_method_name => "MyString", :use_credit => false, :jigyonushi_kari_flg => false, :bank_account_id => 1, :create_user_id => 1, :update_user_id => 1, :team_id => 1 )) end
# ↓ここを追加した fixtures :bank_accounts
it "renders the edit payment_method form" do render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "form", :action => payment_methods_path(@payment_method), :method => "post" do
assert_select "input#payment_method_payment_method_name", :name => "payment_method[payment_method_name]"
assert_select "input#payment_method_use_credit", :name => "payment_method[use_credit]"
assert_select "input#payment_method_jigyonushi_kari_flg", :name => "payment_method[jigyonushi_kari_flg]"
assert_select "input#payment_method_bank_account_id", :name => "payment_method[bank_account_id]"
end
end end [/ruby]
Developmentモードでは正常に処理ができているだけに謎…。
ただ、生成されたテストをよく見てみると、コントローラーは呼ばれていません。アクションメソッドが呼ばれているわけではなく、ただ単純に render メソッドでHTMLが生成されているだけです。また、もう少しよく観察すればわかることだったのですが、テストデータについても before で生成しています。
結局のところテストデータについてはすべて before で生成したものを使用しているのでここでデータを定義してあげる必要があるわけです。 (この事になかなか気づかなかった…)
最終的にテストをパスするコードは下記の通りとなり、すっきりしました。
[ruby]
encoding:utf-8
require 'spec_helper'
describe "payment_methods/edit" do before(:each) do @payment_method = assign(:payment_method, stub_model(PaymentMethod, :payment_method_name => "MyString", :use_credit => false, :jigyonushi_kari_flg => false, :bank_account_id => 1, :create_user_id => 1, :update_user_id => 1, :team_id => 1 ))
# ↓ここに表示用の bank_account データを設定した。設定した内容は stub_model の配列([])にする。
@bank_accounts = assign(:bank_account, [stub_model(BankAccount,
:id => 1,
:bank_name => "テスト銀行",
:branch_name => "テスト支店",
:account_number => "1234567"
)])
end
it "renders the edit payment_method form" do render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "form", :action => payment_methods_path(@payment_method), :method => "post" do
assert_select "input#payment_method_payment_method_name", :name => "payment_method[payment_method_name]"
assert_select "input#payment_method_use_credit", :name => "payment_method[use_credit]"
assert_select "input#payment_method_jigyonushi_kari_flg", :name => "payment_method[jigyonushi_kari_flg]"
# ↓inputタグではなくselectタグを使用する
assert_select "select#payment_method_bank_account_id", :name => "payment_method[bank_account_id]"
end
end end
[/ruby]
普通に当たり前のことなのでしょうが、Railsでは自動生成してすぐ動くので動かすことに目が行ってしまって、テストのソースも十分注意深く読んでいませんでした。生成されるものがどのように生成されているのかということをきちんと理解するようにしないといけないですね。