ActiveSupport_TestCase + shoulda によるテスト のバックアップソース(No.1)

更新

[[公開メモ]]

* ActionController::TestCase + shoulda による Ruby on Rails コントローラのテスト [#ge5f5cd1]

- ActionController::TestCase は Rails 標準のコントローラテストフレームワーク
- [[shoulda>https://github.com/thoughtbot/shoulda]] は読みやすいテストケースを書くための gem

これらを使ってコントローラをテストするためのノウハウをメモとして残したい

* いろいろ検討中 [#rc1df26c]

まだちゃんとしたテストは書き始めたばかりなので全然やり方が定まってません。
このレベルの物をこんなところに晒すのも何なのですが、とりあえずメモとして書いちゃいます。

 LANG:ruby(linenumber)
 #coding: utf-8
 require 'test_helper'
 
 class AccountControllerTest < ActionController::TestCase
   tests AccountController   # この例のように標準的な命名をしている限り、
                             # コントローラ名の指定はしなくてもOK
 
   # すべての context に共通の初期化処理
   setup {
 
     clear_all
 
     # ActionController::TestCase にて、以下のクラス変数が使われている。
     #
     #   @@required_fixture_classes: false
     #   @@already_loaded_fixtures: {}
     #   @@test_suites: {ActiveSupport::TestCase=>true, ...}
     #   @@current: #<AccountControllerTest:0xb192174>
     #
     # クラス変数を使う場合は、これらおよび未来の TestCase のクラス変数と
     # 名前が被らないよう注意する必要あり
 
     # setup とテストケース should の中とで情報をやりとりするのに
     # 普通にインスタンス変数 @xxx を使うか、反則技でクラス変数 @@xxx を
     # 使うのが良いか迷い中
     #
     # インスタンス変数を使うのが普通だが、
     #   - クラススコープからインスタンス変数にアクセスできなくて
     #     残念に感じることがたまにある
     #   - 比較的多数のインスタンス変数がフレームワークによって
     #     使われているため、命名の衝突が怖い
     #
     # クラス変数を使うのはかなり異端
     #   - 本来の目的からずれまくってるので完全に反則技
     #   - 初期化し忘れると直前のテストケースの値が残ってしまうのも問題
     #   - その代わり、どこからでもアクセスできるのがかなり便利?

     @@valid_account_params = { 
       name: "Foo Baa", 
       email: "account@example.com",
       affiliation: "My institute",
       address: "My address",
       telephone: "+00-000-0000"
     }
     
     @@invalid_account_params = {
       name: ''
     }
     
     @@valid_registration_params = {
       gender: 1,
       membership: 1,
       student: false,
       receipt_address: 'My address',
       payaccount: 'My Account'
     }
     
     # インスタンス変数として以下が定義されるので、名前が
     # 被らないように注意が必要
     #
     # インスタンス変数      利用時の記法   内容
     # @__name__             @__name__      現在実行中のテストのタイトルが入る
     # @__io__
     # @passed
     # @_assertions
     # @fixture_cache
     # @fixture_connections
     # @loaded_fixtures
     # @tagged_logger
     # @_partials
     # @_templates
     # @_layouts
     # @_files
     # @request              request        #<ActionController::TestRequest>
     # @response             response       #<ActionController::TestResponse>
     # @controller           @controller    #<Controller under testing>
     # @routes               @routes        #<ActionDispatch::Routing::RouteSet>
     # @shoulda_context
     # @html_document        html_document  #<HTML::Document>
     # @assigns              assigns        @controller 内で定義されたインスタンス変数
   }
 
   def clear_all
     ActionMailer::Base.deliveries.clear
     DatabaseRewinder.clean_all
     session.clear
     cookies.clear
   end
  
   # shoulda の redirect_to では、
   #   should redirect_to account_path
   #   should redirect_to account_path(action: :edit)
   #   should redirect_to url_for(controller: :account, action: :edit)
   # などとは書けないので、かわりに
   #   should redirect_to("account_path"){ account_path }
   #   should redirect_to("account_path(action: :edit)"){ account_path(action: :edit) }
   #   should redirect_to("controller: :account, action: :edit"){ 
   #               @controller.url_for(controller: :account, action: :edit) }
   # としても良いのだけれど、より簡単に
   #   should redirect_to_path :account
   #   should redirect_to_path :account, action: :edit
   #   should redirect_to_path controller: :account, action: :edit
   # などと書けるようにする
   def self.redirect_to_path(*arg)
     if arg.first.is_a? Symbol
       redirect_to_path_arg = arg.dup
       redirect_to_path_symbol = :"#{redirect_to_path_arg.shift}_path"
       if redirect_to_path_arg.empty?
         redirect_to(redirect_to_path_symbol){
           __send__(redirect_to_path_symbol)
         }
       else
         redirect_to_path_arg_s =redirect_to_path_arg.inspect.gsub(/\A\[|\]\z/,'')
         redirect_to_path_arg_s.gsub!(/\A\{|\}\z/,'') if redirect_to_path_arg.count == 1
         redirect_to("#{redirect_to_path_symbol}(#{redirect_to_path_arg_s})", &Proc.new {
           __send__(redirect_to_path_symbol, *redirect_to_path_arg)
         })
       end
     else
       redirect_to_path_arg = arg.dup
       redirect_to_path_arg_s =redirect_to_path_arg.inspect.gsub(/\A\[|\]\z/,'')
       redirect_to_path_arg_s.gsub!(/\A\{|\}\z/,'') if redirect_to_path_arg.count == 1
       redirect_to(redirect_to_path_arg_s){
         @controller.url_for *redirect_to_path_arg
       }
     end
   end
   
   # 以下すべてを ja, en 両方のロケールで試す
   [:ja,:en].each do |locale|
     context "with locale #{locale}," do
       setup {
         request.env['HTTP_ACCEPT_LANGUAGE'] = locale.to_s
       }
       
       # ここからが本当のテスト内容
       
       context "without login," do
 
         context "GET #new" do
           setup { 
             get :new 
           }
           should respond_with :success
           should render_template :new
           should_not set_the_flash
         end
 
         context 'GET #home' do
           setup { 
             get :home 
           }
           should redirect_to_path :account_login
           should set_the_flash
           should 'set session_intended_path to requested path' do
             # セッションキーは ApplicationController で 
             #   @session_intended_path="hogehoge"
             # として与えられているので、assigns を使って読み取る
             assert_equal @request.path, 
                          session[assigns(:session_intended_path)]
           end
         end
         
         context 'invalid POST #create' do
           setup { 
             post :create, account: @@invalid_account_params
           }
           should "not create account" do
             assert_equal 0, Account.count
           end
           should render_template :new
         end
         
         context 'valid POST #create' do
           setup {
             post :create, account: @@valid_account_params
           }
           should "create account" do
             assert_equal 1, Account.count
           end
           should redirect_to_path :account_login
           should "send email to registered address" do
             assert_not ActionMailer::Base.deliveries.empty?
             email_sent = ActionMailer::Base.deliveries.last
             assert_equal @@valid_account_params[:email], email_sent.to
           end
         end
         
       end
 
       context "with autologin," do
 
         setup {  
           clear_all
           @@account = Account.create!(
             @@valid_account_params.merge(
               password: Account.generate_new_password,
               auto_login: Account.generate_new_password
             )
           )
           
           cookies[assigns(:cookie_auto_login)] =
               [@@account.auto_login, @@account.email].join(':')
         }
         
         context 'GET #home' do
           setup { 
             get :home 
           }
           should respond_with :success
           should 'update autologin password at successful auto login' do
             assert_not_equal @@account.auto_login, assigns[:account].auto_login
             assert_equal assigns[:account].auto_login, 
                          cookies[assigns[:cookie_auto_login]].split(':',2)[0]
           end
         end
         
         context 'GET #new' do
           setup { 
             get :new
           }
           should set_the_flash  # already login
           should redirect_to_path :account
         end
         
         context 'GET #edit' do
           setup { 
             get :edit
           }
           should respond_with :success
         end
         
         context 'POST #update' do
           setup {
             post :update, account: {name: "Updated Name"}
           }
           should 'update name' do
             account = Account.find_by_email(@@account.email)
             assert_equal "Updated Name", account.name
           end
           should set_the_flash  # successfully updated
           should redirect_to_path :account
         end
         
         context 'DELETE #destroy' do
           setup {
             delete :destroy
           }
           should "delete account" do
             assert_equal 0, Account.count
           end
           should set_the_flash  # successfully deleted
           should redirect_to_path :default
         end
         
         context 'with valid registration,' do
           setup {
             @@account.registration =
               Registration.create!(@@valid_registration_params)
           }
 
           context 'DELETE #destroy' do
             setup {
               delete :destroy
             }
             should "not delete account" do
               assert_equal 1, Account.count
             end
             should set_the_flash  # can not delete
             should redirect_to_path :account
           end
         end
 
       end
     end
   end
   
 end

* コメント [#yfa709ad]

#article_kcaptcha

Counter: 8412 (from 2010/06/03), today: 4, yesterday: 0