そのコードはテストされているか?
テストコードを書いていると、ふと疑問を感じて心配になることがある。
- 果たして、このテストコードで漏れなく動作確認できているのだろうか?
- テストが漏れているところが、どこかに無いだろうか?
特にテストを書くことに慣れていない現状では、何処でどんなテストを書いておくべきかも手探りだ。そんな時rcovが勇気づけてくれた。
使い方
- 開発中のRailsプロジェクトのルートで以下のコマンドを実行してみた。
$ cd ~/railsapp/test_slip202 $ rcov -x Library/Ruby/Gems --rails test/**/*_test.rb
- コマンドが完了すると、test_slip202/coverageフォルダ以下にHTMLファイルが生成されている。index.htmlを開いてみる。
- 一番下の行app/models/slip.rbのCode coverageを見ると「61.2%」と表示されている。つまり、実行コードのうち「61.2%」は何らかのテストがされているが、残りの「38.8%」は全くテストされていないということだ。
- app/models/slip.rbのリンクをクリックすると、コードの詳細を確認できた。
- テストされていないコードについては、赤で示されている。確認してみると、挿入・削除・コピー関連のメソッドが赤くなっていた。確かにテストコードをまだ書いていなかった...。
- 無駄が多い気がするが、以下のようにテストを追加してみた。
# テスト: test/units/slip_test.rb require File.dirname(__FILE__) + '/../test_helper' class SlipTest < ActiveSupport::TestCase # Replace this with your real tests. def test_truth assert true end def test_invalid_with_empty_attributes slip = Slip.new journals = slip.make_journals("j123456789"=>{}) assert !slip.valid? assert slip.errors.invalid?(:number) assert slip.errors.invalid?(:executed_on) assert slip.errors.invalid?(:total_yen) assert slip.errors.invalid?(:base) end def test_invalid_with_not_equal_total_yen slip = Slip.new(:number=>"1", :executed_on=>"2/1", :total_yen=>"2000") journals = slip.make_journals("j123456781"=>{:comment=>"test", :yen=>"1000"}) assert !slip.valid? assert_equal "Total yenが明細の合計と一致していません。", slip.errors.on(:total_yen) end def test_copy_journal slip = Slip.new(:number=>"1", :executed_on=>"2/1", :total_yen=>"1000") journals = slip.make_journals("j123456789"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"j123456789"}) journals_count = slip.editing_journals.size original_journal = journals[0] copy_journal = slip.copy_journal("j123456789") assert_equal slip.editing_journals.size, journals_count + 1 assert_equal original_journal.comment, copy_journal.comment assert_equal original_journal.yen, copy_journal.yen assert_equal slip.editing_journals[0].index, "j123456789" assert_not_equal slip.editing_journals[1].index, "j123456789" end def test_insert_journal slip = Slip.new(:number=>"1", :executed_on=>"2/1", :total_yen=>"1000") journals = slip.make_journals("j123456789"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"j123456789"}) journals_count = slip.editing_journals.size original_journal = journals[0] insert_journal = slip.insert_journal("j123456789") assert_equal slip.editing_journals.size, journals_count + 1 assert_nil insert_journal.comment assert_nil insert_journal.yen assert_not_equal slip.editing_journals[0].index, "j123456789" assert_equal slip.editing_journals[1].index, "j123456789" end def test_delete_journal slip = Slip.new(:number=>"1", :executed_on=>"2/1", :total_yen=>"3000") journals = slip.make_journals("j123456781"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"j123456781"}, "j123456782"=>{:comment=>"test", :yen=>"2000", :position=>2, :index=>"j123456782"}) journals_count = slip.editing_journals.size delete_journal = slip.delete_journal("j123456782") assert_equal slip.editing_journals.size, journals_count - 1 assert_nil slip.journal_at("j123456782") end end
- 再び、rcovを実行してみる。
$ rcov -x Library/Ruby/Gems --rails test/**/*_test.rb
ブラウザでcoverage/index.htmlを再読み込みしてみると...app/models/slip.rbのCode coverageは100%になった。ひとまず安心できた!
コントローラーの追加テスト
- 同じようにコントローラーのテストにも漏れがあったので、以下のように追記してみた。
- create、updateが検証エラーで登録できなかった場合のテストを追加した。(このテストについては全く忘れていたので、rcovで発見できて良かった。)
- 明細行の挿入・削除・コピーのテストを追加した。
require File.dirname(__FILE__) + '/../test_helper' class SlipsControllerTest < ActionController::TestCase ...(中略)... def test_should_not_create_slip post :create, {:slip =>{:number=>'3', :executed_on=>'2/20', :total_yen=>'3000'}, :journal=>{"1"=>{:yen=>"1000", :index=>"j943792543", :position=>"1", :comment=>"test3"}} } assert_response :success assert_template 'new' end def test_should_not_update_slip put :update, {:id =>'1', :slip =>{:number=>'1', :executed_on=>'2/14', :total_yen=>'10,000'}, :journal=>{"1"=>{:yen=>"1,000", :index=>"j943792544", :position=>"1", :comment=>"test"}} } assert_response :success assert_template 'edit' end def _test_should_insert_row xhr :post, :insert_row, {:index =>'1', :slip =>{:number=>"1", :executed_on=>"2/1", :total_yen=>"1000"}, :journal=>{"1"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"1"}} } assert_response :success assert_select 'tbody[id^=j9] tr' do assert_select 'th', 2 assert_select 'td', 2 end assert_select 'tbody#journals_footer tr' do assert_select 'th', 3 assert_select 'th input[value=1,000]', 1 end end def _test_should_copy_row xhr :post, :copy_row, {:index =>'1', :slip =>{:number=>"1", :executed_on=>"2/1", :total_yen=>"1000"}, :journal=>{"1"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"1"}} } assert_response :success assert_select 'tbody[id^=j9] tr' do assert_select 'th', 2 assert_select 'td input[value=test]', 1 assert_select 'td input[value=1,000]', 1 end assert_select 'tbody#journals_footer tr' do assert_select 'th', 3 assert_select 'th input[value=2,000]', 1 end end def _test_should_delete_row xhr :post, :delete_row, {:index =>'1', :slip =>{:number=>"1", :executed_on=>"2/1", :total_yen=>"1000"}, :journal=>{"1"=>{:comment=>"test", :yen=>"1000", :position=>1, :index=>"1"}, "2"=>{:comment=>"", :yen=>"", :position=>2, :index=>"2"}} } assert_response :success assert_select 'tbody#journals_footer tr' do assert_select 'th', 3 assert_select 'th input[value=0]', 1 end end end
うーん...相変わらず悩みながらのテストだ...。
- 果たして、上記のテストコードでちゃんとテストできているのだろうか?
- rcovはテストコードによって一度でも実行された部分はOKとしてくれるようだが、テストでどんなチェックをしておくべきかは作っている人間のみが知っている...。
- Code coverageが100%だからといって、単純に安心していてはいけないことに気付いた。
- もっともっとテストの達人が書いたコードを勉強しておく必要がある。
参考ページ
以下のページがたいへん参考になりました。感謝です!
- coverage
- 保証範囲とか、普及率、取扱い範囲などの意味を持っているようだ。