2015年10月1日木曜日

Ruby on Railsで、実際にありそうなシーン別にテストコードを書いてみる

こんにちは。
鷲尾です。


現在Ruby on Railsに触れ始めて少し経ちますが、個人的には非常にとっつきにくく、理解に時間がかかることがたくさんあります。

例えば、テストコードを作成する際に、遷移先ページに表示されたことをテストするためにはどうすればよいのか、なにを書けばいいのかなど、戸惑うことがあるかと思います。(URLが指定通りに変化しているのか、正しい値がセットされているかなど・・・)

バリバリのプログラマーの方であればなにをそんなことと思うかもしれませんが、同じ初心者の方にもわかりやすいよう、シーン別にこのあたりを少しまとめてみようと思います。

そこで今回は、Ruby on Railsでテストコードを作成する際に、比較的多く使用されるマッチャーの使い方をシーン別に紹介していきたいと思います。


今回紹介するものは
1.なにかボタンを押されたりして、きちんと指定した遷移先に遷移しているかどうかをテストしたい
2.指定した文字列のメッセージが表示されているかどうかをテストしたい
3.登録処理などを行ったときに、本当にDBに登録されているかをテストしたい

といった3つのシーンを考えてみます。

※今回はRspecでテストを行っています。また、画面での操作をシミュレーションすることが出来るCapybaraというパッケージを導入していますので、事前に導入しておいてください。
参考:http://www.oiax.jp/rails/rspec_capybara_primer/rspec_capybara_the_first_step.html


1.きちんと指定した遷移先に遷移しているかどうかをテストしたい
例えば、ログイン画面でIDとパスワードを入力したあとに、正しい認証情報だったらトップ画面に遷移させたいといったことがあると思います。

こういった場合、例えば遷移後のURLを確認することで、テストを行うことが出来ます。

では、例文です。

let(:main_page_url){
  current_host << '/main_page'
}

let(:default_params){
  user_id => 'abcde1234'
}

  scenario 'ログイン後にmain_pageに遷移するか' do
      visit "ログインページURL"
      user = User.create default_params
     
      fill_in 'user_id', with: default_params[:user_id] ← 注1 
      click_button "ログイン"
         
      expect(current_url).to eq(main_page_url)
end

注1 ここでfill_in 'user_id'とありますが、これは入力を行うテキストボックスのIDを指しています。
例えば、ログインページにユーザIDを入力するテキストボックスがあり、そのテキストボックスにuser_idというIDが設定されていたとします。その際、Capybaraのfill_inメソッドを使って"user_idというIDがついたテキストボックス"に値を入力させることができます。



テストコードを実行する前にmain_page_urlなどの名前で遷移先のURLなどを設定しておきます。
※テストコードが書かれているファイルの初めに宣言しておくイメージです。
今回で言うと、main_page_url、default_paramsになります。

動作的には、遷移後のURLが設定したmain_page_urlと等しいかどうかを確認します。
current_urlには現在表示しているページのURLが格納されているため、

expect(今いるページのURL).to eq(遷移先のURL) 

となるわけです。
こうすれば、URLを比較することができますね。


2.指定した文字列のメッセージが表示されているかどうかをテストしたい
では、例えばなにかエラーメッセージが表示されるようにするときや、リダイレクト先にきちんと想定している文字列が表示されているかどうかを確認したいことがあると思います。
そういった場合は、上記のURLだけでなく、そのページに意図した文字列がきちんと表示されているかどうかを確認する必要があります。
例えば、ログイン画面を作っていて、ユーザIDに不適切な値を入力されたままログインボタンを押した場合。こういった場合、ページを遷移せずに、入力フォームの上や下などにエラーメッセージを表示することがありますね。





この場合、例えばこのように書くことができます。


let(:main_page_url){
  current_host << '/main_page'
}

let(:default_params){
  user_id => 'abcde1234'
}

let(:login_page_url){
  current_host << '/login_page'
}

let(:miss_params){
  user_id => 'abcde4321'
}
  
  scenario 'ログインに失敗した場合、ユーザ名は必須項目です!が表示されるか' do
      visit "ログインページURL"
      User.create default_params

      user = User.first



      fill_in 'user_id', with: miss_params[:user_id]

      click_button "ログイン"
         

      expect(current_url).to eq(login_page_url)  ← 注2
      expect(page).to have_content('ユーザ名は必須項目です!')
end
注2 ログインページにアクセスした状態が"/login_page"だったとして、ログイン失敗後も"/login_page"であるとしています。

ここで使用しているhave_contentというマッチャは、テスト対象のページに指定した文字列が存在するかどうかを確認します。

したがって、現在いるページの上部にエラーメッセージが表示される場合など、比較対象のページが現在いるページと同じページで、エラーメッセージだけが追記されるという場合に、よく利用されます。


3.登録処理などを行ったときに、本当にDBに登録されているかをテストしたい
実際にDBになにか登録する際に、本当に登録されているかどうかを確認するシーンがあると思います。
そう言った際に、どうすればDBに値が登録されているかを確認することができるのでしょうか。
この場合、登録先のDBのレコードが+1されているかどうかを確認します。
また、同じように、データ(レコード)の削除を行うメソッドの場合、DBからレコードが-1されているかどうかを確認することが出来ます。

例えば、以下のようになります。

let(:valid_attributes) {
  {
    :user_id => "abcde1234"
  }
}

it "ユーザ情報を1件登録出来ていること" do

 expect {

         post :create, {:user => valid_attributes}

     }.to change(User, :count).by(1)

 end

この場合、postでcreateメソッドが実行された際に、UserというDBにレコードが1件追加されているかどうかを確認しています。

同じように、DBから該当のレコードが削除される場合は、以下のように書きます。

let(:valid_attributes) {
  {
    :user_id => "abcde1234"
  }
}

it "ユーザ情報を1件登録出来ていること" do
 expect {

         post :delete, {:user => valid_attributes}

     }.to change(User, :count).by(-1)
 end

こうすると、レコードが1件減ったという状態をテストすることができるので、正常に削除されたかどうかをテストすることが出来ます。


Ruby on Railsでは、テストコード内で様々な状態をシミュレートするためのマッチャー(matchers)が他にもたくさん用意されています。

私も何度も経験していますが、はじめに「なにをテストしたいのか」ということを明確にしておかないと、テストコードを作成しても確認したいことが確認できていないテストコードが出来上がってしまいます。
当たり前のように聞こえるかもしれませんが、なにを確認したくて、そのためにはどのマッチャを使う必要があるのかをきちんと考えてから、テストコードを作成すると良いかと思います。

今回紹介したテストコードの例が少しでも参考になれば幸いです。

以上です。





0 件のコメント:

コメントを投稿