テスト

Railsにはデフォルトで自動テストがいろいろあるんだねー

まずは、TEST用DBセットアップ

  • あー、なるほど。test環境って、自動テスト用だったんだね。
  • まあ、初めにこんなコマンドでtest用DB環境を初期化する、と
    rake db:migrate
    rake db:test:load
    
    • へー。「マイグレーション」で出てきたfixturesは、テスト実行時に自動で展開されるんだ!これは、地味に便利かも

テストの実行方法

  • Unitテストを、単発で実行するにはこんな感じで。
    rake test:units TEST='test/unit/book_test.rb'
    
    • これは、book_test.rbを単発で実行する場合ね!
      • うーん、なんだけど、テストを実行したら[rake aborted!]しちゃうなー。minitestでエラーっぽいんだが…。
      • あ、判った!testで使ってる[minitest]ってパッケージが悪さしてたんだ!最新(2.11.0)を入れて古い1.6.0を消したら動いた!
  • Unitテスト全体を実行する場合は、こんなんで
    rake test:units
    
  • 更に、全てのテストを実行する場合は、こう!
    rake test
    

Unitテスト

  • いわゆるxUnit系のテストね。
    • まずは、テストを書くんだけど、とりあえず今までに作ったモデルのテストファイルだけは、既に[test/unit]の下にできてる
    • なんで、まずはBookのsaveテストを書いてみる
      #coding: utf-8
      
      require 'test_helper'
      
      class BookTest < ActiveSupport::TestCase
       test 'book save' do
         book = Book.new({
           :isbn => '111-2-3333-4444-5',
           :title => 'テストの本!',
           :price => 100,
           :publish => 'テスト出版社',
           :published => '2012-01-01',
           :cd => false
         })
         assert book.save, 'Suuccess to save'
       end
      end
      
      • このtestメソッドの'book save'は、内部的にテストメソッド名として使われるから、一意にしないと駄目
      • ただ、こんな感じで日本語を書いても動くことは動く(Windows環境だと、化けるけどw)
         test '日本語、行けるかな?' do
           assert true
         end
        
      • しかも、assertメソッド、assert_equalとは別だった事は地味に驚いたw(正確には、assert_trueだと思うが…、まあ基本はtrueでもいいかー)
  • で、バリデートのテストなんかは、こんな感じでー
     test 'book validate' do
       book = Book.new({
         :isbn => '111-2-3333-44',
         :title => '',
         :price => 'abc',
         :publish => '',
         :published => '2000-01-01',
         :cd => false
       })
       assert !book.save
       assert_equal book.errors[:isbn], ['is the wrong length (should be 17 characters)']
       assert_equal book.errors.size, 3 # 全体で3件のエラー
       assert book.errors[:title].any?  # titleで何かしらのエラー
     end
    
  • 検索結果のテストは、こんな感じ!
     test 'book where method test' do
       result = Book.where(:title => 'JavaScript本格入門').first
       assert_instance_of Book, result                     # resultが、Bookオブジェクトか?
       assert_equal books(:js).isbn, result.isbn           # books.ymlの:jsキーの値と、resultのjsが同じか?
       assert_equal Date.new(2010,11,26), result.published # result.publishが、2010/11/26か?
     end
    
    • この中で使ってる「books(:js)」ってすげー!
      • これ、Railsが初期データとして「books.yml」を取り込んで、ハッシュに展開してるんだ!
      • booksは、対応するモデルのオブジェクトなんで、データ型も一緒なんだねー→要するに日付型もそのままassert_equalできるってことね!
  • トランザクションのテストは、気をつけよう!
    • Railsのテストは、基本テスト後にロールバックしてるので、そのままではトランザクションのテストはできん!
    • なので、どーしてもやりたい時は、self.use_transactional_fixtures=falseを設定しないと駄目みたい
    • うーん、これやる時は要注意だなー
  • setupとteardownもあるよー
    • まあ、そのままですな。
    • ただ、DBクリアとフィクスチャの読み取りはRailsがやってくれるから、結構、楽かも!

Functionalテスト

  • Functionalテストは、コントローラのテストね!
    • 例によって、デフォルトのテストファイルは[test/functional]できてるはず
    • なんで、そこの[hello_controller_test.rb]を、こんな感じで書けばOK!
      require 'test_helper'
      
      class HelloControllerTest < ActionController::TestCase
       test 'list action' do
         get :list # これで、HTTP GETでlistアクションを実行
         len = assigns(:books).length # コントローラで設定された:booksの件数を取得
         assert_equal 10, len         # :booksの件数が10件であること
         assert_response :success     # レスポンスコードが、200であること
         assert_template 'hello/list' # 'hello/list'のテンプレートを使うこと
       end
      end
      
    • あとは、Unitテストと同様に、こんなコマンドでテスト実行!
      rake test:functionals TEST='test/functional/hello_controller_test.rb'
      
      • なんだけど、うーん、エラーになる…。どーも、フィクスチャのデータが入ってない?それとも、assigns(:books)が効いてない?画面ではちゃんとlistで一覧出てるしなー。
      • assigns(:bools)がnilだ…。viewアクションの:msgでは固定値入れてるのにnilだし、assigns(:books)が効いて無い感じだな…
      • ああああ!判った!!「フィルタ」のトコで作ったログインが効いてて、NGになってるだけなんだ…。なので、ログインを無効にして動いた…。本気でださい…。
      • そっか、アクションのテストだから、当たり前だけどapplication_controller.rbも動いてるんだ。そらそうか…。
    • で、上記のgetメソッドの他にも、post/put/delete/headもあるよー
      • getでは、「get :show, {:id => 10}」とかいろんなパラメータ(セッションやフラッシュも)が設定可能ね!
  • functionalテストのassertは、面白いのがいろいろあるなー
    • assert_differenceは、ブロック処理した後、式の値が変化してるかをチェックするんだね!
      • books_controller_test.rbでの登録テストはこんな感じね!
         test 'diff check' do
           assert_difference 'Book.count', 1 do
             post :create, :book => {
               :isbn => '111-2-3333-4444-5',
               :title => 'テストの本!',
               :price => 100,
               :publish => 'テスト出版社',
               :published => '2012-01-01',
               :cd => false
             }
           end
         end
        
      • これだと、Book.countが、:createした後に、1増えてる事を確認してる感じだね。
    • assert_generatesは、ルーティングをチェックするのにいいね。次の例は、hello/listね。
       test 'routing check' do
         assert_generates 'hello/list', {:controller => 'hello', :action => 'list'} 
       end
      
    • assert_selectは、ビューのテスト用ね!こんな感じで、結構いろいろ出来そう!
       test 'select check' do
         get :list # これで、HTTP GETでlistアクションを実行
         assert_select 'title'       # (1)<title>タグがあるか?
         assert_select 'title', true # (2)上と同じ
         assert_select 'font', false # (3)<font>が無いか?
         assert_select 'title', 'Railbook'    # (4)<title>下のテキストが「Railbook」か?
         assert_select 'title', /[A-Za-z0-9]/ # (5)<title>下のテキストが英数字か?
         assert_select 'script[type=?]', /.+/ # (6)<script>のtypeが何かあるか?
         assert_select 'table tr[style]', 10  # (7)<table>のstyleのある<tr>が、10個あるか?
         assert_select 'table' do             # (8)<table>のstyleのある<tr>が、1~10まであるか?
           assert_select 'tr[style]', 1..10
         end
         assert_select 'title', {:count => 1, :text => 'Railbook'} # (9) <title>が1つで、'Railbook'か?
       end
      
      • (6)の「type=?」の?は、プレイスホルダね。
      • (8)みたいに、ブロック指定で入れ子にできるのは、すごいなー。
      • (9)は、ハッシュを使って一気にassertするんだ。他にも、:html/:minimum/:maximumなんかも使えるみたい!

Integrationテスト

  • 要するに、画面遷移なんかのテストだね!
  • これはまだ自動生成されてないから、以下の手順で作って、テストする感じみたいです。
    • この例は、hello/viewにアクセスして、未ログインだからログインしてから、hello/viewにリダイレクト、みたいな例ね。
    • まずは、以下のコマンドでテストスクリプトを作成
      rails g integration_test admin_login
      
    • で、テストスクリプト(test/integration/admin_login_test.rb)を、こんな感じで修正
       test 'login test' do
         # /hello/viewを GETでアクセス
         get '/hello/view'
         assert_response :redirect                   # レスポンスが、リダイレクトであること
         assert_redirected_to '/login'               # リダイレクト先が、/loginであること
         assert_equal '/hello/view', flash[:referer] # フラッシュの:refererに、現在のURL'/hello/view'が格納されている事
      
         # 直前のリダイレクトのリクエストを処理→リダイレクトしてログイン画面へ
         follow_redirect!
         assert_response :success                    # レスポンスが成功であること
         assert_equal '/hello/view', flash[:referer] # フラッシュの:refererに、遷移先の'/hello/view'が格納されている事
      
         # ユーザ/パスワードを入れて、ログイン
         put '/login/auth', {:username => 'abc', :password => 'cdf', :referer => '/hello/view'} 
         assert_response :redirect                                      # レスポンスが、リダイレクトであること
         assert_redirected_to :controller => 'hello', :action => 'view' # アクションがhello#viewであること
      
         # 直前のリダイレクトのリクエストを処理→リダイレクトして/hello/viewへ
         follow_redirect!
         assert_response :success                    # レスポンスが成功であること
       end
      
    • 後は、Functionalテストと同じように、こんなコマンドでテスト実行
      rake test:integration TEST='test/integration/admin_login_test.rb'
      
  • うーん、でも、これ、使うかな?さすがにこの辺はSeleniumでやったほうがいいような気がするなー。

Performanceテスト

  • へー、パフォーマンステストまであるんだ。すごいなー
  • まあ、この辺は必要になったらね!

-
最終更新:2012年02月04日 07:59