on Rails : login_generatorを本格的に使ってみる2

[Ruby]on Rails : login_generatorを本格的に使ってみる1の続きです.コッチは比較的ソース多めなので,勘弁してください.

モデルのテスト

それでは,先ずはテスト用のtest/fixture/users.ymlを弄ります.ユーザ名を追加したので,ちゃんとfixtureにも追加します.

izumi:
  id: 1
  name : 泉野明
  login: izumi
  password: <%= User.sha1("test1") %>
  
asuma:
  id: 2
  name : 篠原遊馬
  login: asuma
  password: <%= User.sha1("test2") %>
  
isao:
  id: 3
  name : 太田功
  login: isao
  password: <%= User.sha1("test3") %>

次に,テストケースも弄ります.まずはモデルUserのテストケースから.test/unit/user_test.rbを開き,編集.折角ですから,ユーザ名が入力されていなかったときのテストケースも先に作っちゃいましょう.ひとつだけ,例を載せます.

  def test_disallowed_passwords
    u = User.new    
    u.login = "gotou"
    u.name = "後藤喜一"

    u.password = u.password_confirmation = "tiny"
    assert !u.save     
    assert u.errors.invalid?('password')

    u.password = u.password_confirmation = "huge" * 100
    assert !u.save     
    assert u.errors.invalid?('password')
        
    u.password = u.password_confirmation = ""
    assert !u.save    
    assert u.errors.invalid?('password')
        
    u.password = u.password_confirmation = "secure_password"
    assert u.save     
    assert u.errors.empty?
  end

ちゃんとtest_createやtest_bad_password,test_bad_loginにもu.nameを書き,適当に作れたら,モデルだけのテストを行う.

$ rake test:units

さて,test_bad_namesの部分でエラーが出たはずですから,次はモデルの修正をします.

  validates_length_of :name, :within => 4 .. 40 # 追加 
  validates_presence_of :login, :password, :password_confirmation, :name # :nameを追加

こうするだけ.もう一度rake test:unitsをすると,通ります.

全体のテスト

次は普通にrakeを実行してみます.すると,やたらとassert_session_hasで,セッションにuserなんてねーよって怒られるはず.ちなみに,test_auth_bobとかいうのはtest_loginみたいなもんだから,こんなワケの分からん名前は変えましょう.とりあえず,テストケースはこうなりました.

require File.dirname(__FILE__) + '/../test_helper'
require 'account_controller'

# Raise errors beyond the default web-based presentation
class AccountController; def rescue_action(e) raise e end; end

class AccountControllerTest < Test::Unit::TestCase
  self.use_instantiated_fixtures  = true #忘れずに!
  
  fixtures :users
  
  def setup
    @controller = AccountController.new
    @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
    @request.host = "localhost"
  end
  
  def test_login
    post :login, :user => {:login => "asuma", :password => "test2"}
    assert_session_has :user

    assert_equal @asuma, @response.session[:user]
    
    assert_redirected_to :action => "welcome"
  end
  
  def test_signup
    post(:signup,
         :user => { :name => "熊耳武緒", :login => "takeo",
           :password => "newpassword", :password_confirmation => "newpassword" })
    assert_session_has :user
    
    assert_redirected_to :action => "welcome"
  end

  def test_bad_signup
    post :signup, :user => { :name => "新ボブ", :login => "newbob", 
    :password => "newpassword", :password_confirmation => "wrong" }
    assert_invalid_column_on_record "user", "password"
    assert_success
    
    post :signup, :user => { :name => "よ", "login" => "yo", 
    :password => "newpassword", :password_confirmation => "newpassword" }
    assert_invalid_column_on_record "user", "login"
    assert_success

    post :signup, :user => { :name => "よよよ", :login => "yo", 
    :password => "newpassword", :password_confirmation => "wrong" }
    assert_invalid_column_on_record "user", ["login", "password"]
    assert_success
  end

  def test_invalid_login
    post :login, :user => {:login => "izumi", :password => "machigai"}
     
    assert_session_has_no :user
  end
  
  def test_login_logoff
    post :login, :user => {:login => "izumi", :password => "test1"}
    assert_session_has :user

    get :logout
    assert_session_has_no :user
  end
end

rakeを実行すると,先ず間違いなく失敗すると思います.

ビューを直そう

次に,ビューを弄ります.弄らなければ行けないのは,singupとloginだけ.まずはsignup.rhtmlから.

<%= start_form_tag :action=> "signup" %>
<div title="Account signup" id="signupform" class="form">
  <h3>サインアップ</h3>
  <%= error_messages_for 'user' %><br/>
  <label for="user_name">お名前:</label><br/>
  <%= text_field "user", "name", :size => 30 %><br />
  <label for="user_login">ご希望のログインネーム:</label><br/>
  <%= text_field "user", "login", :size => 30 %><br/>
  <label for="user_password">パスワード:</label><br/>
  <%= password_field "user", "password", :size => 30 %><br/>
  <label for="user_password_confirmation">パスワード確認:</label><br/>
  <%= password_field "user", "password_confirmation", :size => 30 %><br/>
<%= submit_tag 'サインイン' %>
<%= end_form_tag %>

名前入力欄を追加しました.


つぎにlogin.rhtmlを.

<%= start_form_tag :action=> "login" %>
<div title="Account login" id="loginform" class="form">
    <h3>ログインしてください</h3>
    <% if @message %>
      <div id="message"><%= @message %></div>
    <% end %>
    <p><label for="user_login">ログイン:</label><br/>
    <%= text_field 'user', 'login' %></p>
    <p><label for="user_password">パスワード:</label><br/>
    <%= password_field 'user', 'password' %></p>
    <%= submit_tag 'ログイン' %>
    <br/>
</div>
<%= end_form_tag %>

パラメータを@param[:user][:login]みたいな形にしたかったので,こうしました.こうするとコントローラからも取り易いし,わかり易いし.

コントローラも直そう

最後に,コントローラの編集.

class AccountController < ApplicationController
  model   :user
  layout  'scaffold'

  def login
    case @request.method
      when :post
        if @session[:user] = User.authenticate(@params[:user][:login], @params[:user][:password])
          
          flash[:notice]  = "Login successful"
          redirect_to :action => "welcome"
        else
          @login    = @params[:login]
          @message  = "Login unsuccessful"
      end
    end
  end
  
  def signup
    case @request.method
      when :post
        @user = User.new(@params[:user])
        
        if @user.save      
          @session[:user] = User.authenticate(@user.login, @params[:user][:password])
          flash[:notice]  = "Signup successful"
          redirect_to :action => "welcome"          
        end
      when :get
        @user = User.new
    end      
  end  
  
  def delete
    if @params[:id]
      @user = User.find(@params[:id])
      @user.destroy
    end
    redirect_to :action => "welcome"
  end  
    
  def logout
    @session[:user] = nil
  end
    
  def welcome
  end
end

いきなりdeleteとか出来てますが,ここは気にしちゃ駄目だと思うぜ.俗に言う親切心ってやつだもんな.


さて,これで出来上がり.rakeをしてみると通ります.


以上で,ごく簡単なlogin_generatorの使い方を終わらせてもらいます.

総括

確かに自動でログイン周りを作ってくれるのは,とても有り難いことだ.しかし,テストケースが始めからできているというのも困った話で,テストケースに関しては,ほとんど一から自分で書くのと変わらないぐらいの労力が必要な気もする.全体を通してみると,楽なのは楽,なのかな?