カフェでプログラミング
http://d.hatena.ne.jp/h_gomi/20071117/1195305270
で書いたカフェプログラミングの続きを友達と一緒にやりました。
今日は家の近くのカフェで。
(http://d.hatena.ne.jp/h_gomi/20071118/1195395227
で行ってバナナマフィン食べたところ)
ランチでパスタ食べて、3時ごろにバナナマフィン食べました。
1150円で3時間半粘っておいしくておなかいっぱいで、
ネット使えるので満足です。家から徒歩1分というのもすばらしい!
さて本日は状態遷移テストに取り組みました。
簡単に動くコード書いて、
DRYに変えて、テスト作って。
テスト駆動までは行ってないけど、プログラミングとして
いい感じに進みました。
今日作ったコードの一部を公開しようと思いますが、
その前に本日のTipsを。
- コントローラのテストでは、セッションへのsetが必要
- エディタの文字コードはテスト前に要チェック。特に日本語使う場合。
- コマンドプロンプトの文字コードを変える(utf-8):chcp 65001
- HTTPのステータスが謎。テストにて、未ログイン⇒302、ログイン済⇒success(200)
- javascript、要素をidで取得 f.issue_status.value='#{status}';
- DRY
課題管理アプリなので中心となるモデルはIssueくん。
class Issue < ActiveRecord::Base HUMANIZED_ATTRIBUTE_KEY_NAMES = {'Title' => 'タイトル', 'Category' => 'カテゴリ', 'Priority' => '優先度', 'Description' => '説明', 'Next check date' =>'次回チェック日', 'Status' => 'ステータス'} attr_accessor :db_status def self.japanese_col_name english_col_name HUMANIZED_ATTRIBUTE_KEY_NAMES[english_col_name] end validates_length_of :title, :maximum=>100, :message =>'は100桁以内で入力してください' validates_presence_of :title, :message =>'は必須です' validates_format_of :title, :message =>'は必須です。記号はハイフン「-」 アンダーバー「_」のみ使えます', :with => /^[\wa-zA-Z0-9\-_()\\!?]*$/ def self.statuses ["未着手", "対応中", "回答待", "リリース待", "完了"] end def next_statuses case db_status when '未着手' ['未着手','回答待','対応中','完了'] when '回答待' ['回答待','対応中'] when '対応中' ['回答待','対応中','リリース待'] when 'リリース待' ['リリース待','完了'] when '完了' ['未着手','回答待','対応中','リリース待','完了'] else ['未着手','回答待','対応中','リリース待','完了'] end end end
そしてIssueくんのテスト
require File.dirname(__FILE__) + '/../test_helper' class IssueTest < Test::Unit::TestCase fixtures :issues def test_validation_category issue = Issue.new(:category => '適当なカテゴリ', :priority=>'適当',:description=>'適当',:next_check_date=>Time.now,:status=>'てきとー') # タイトルが101文字の場合 issue.title = "あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ" assert !issue.valid? # タイトルが100文字の場合 issue.title = "ああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ" assert issue.valid? # タイトルが1文字の場合 issue.title = "あ" assert issue.valid? # タイトルが0文字の場合 issue.title = "" assert !issue.valid? # タイトルが半角スペースのみの場合 issue.title = " " assert !issue.valid? # タイトルが全角スペースのみの場合 issue.title = " " assert issue.valid? # タイトルが半角スペースのみの場合 issue.title = " " assert !issue.valid? # タイトルがタブの場合 issue.title = "\t" assert !issue.valid? # タイトルがタブの場合 issue.title = ' ' assert !issue.valid? assert_equal issue.errors[:title].first, 'は必須です。記号はハイフン「-」 アンダーバー「_」のみ使えます' end def test_next_status # 新規作成の場合 issue = Issue.new(:status => nil) assert_equal issue.next_statuses,['未着手','回答待','対応中','リリース待','完了'] # 既存ステータスが未着手の場合 issue = Issue.new(:db_status => '未着手') assert_equal issue.next_statuses,['未着手','回答待','対応中','完了'] # 既存ステータスが回答待の場合 issue = Issue.new(:db_status => '回答待') assert_equal issue.next_statuses,['回答待','対応中'] # 既存ステータスが対応中の場合 issue = Issue.new(:db_status => '対応中') assert_equal issue.next_statuses,['回答待','対応中','リリース待'] # 既存ステータスがリリース待の場合 issue = Issue.new(:db_status => 'リリース待') assert_equal issue.next_statuses,['リリース待','完了'] # 既存ステータスが完了の場合 issue = Issue.new(:db_status => '完了') assert_equal issue.next_statuses,['未着手','回答待','対応中','リリース待','完了'] end end
あとはコントローラとそのテスト
require 'hikidoc' class KanriController < ApplicationController def index list render :action => 'list' end # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => :post, :only => [ :destroy,:create, :update ], :redirect_to => { :action => :list } def list @check_date = DateTime.now @check_date = DateTime.new(params[:date][:year].to_i, params[:date][:month].to_i,params[:date][:day].to_i,params[:date][:hour].to_i,params[:date][:minute].to_i) if params[:date] @issue_pages, @issues = paginate(:issues, :conditions => ["next_check_date <= ?", @check_date], :per_page => 10) end def show @issue = Issue.find(params[:id]) end def new if params[:issue] @issue = Issue.new(params[:issue]) else @issue = Issue.new end end def create @issue = Issue.new(params[:issue]) if @issue.save flash[:notice] = '課題は正常に登録されました。' redirect_to :action => 'list' else render :action => 'new' end end def edit if params[:issue] @issue = Issue.new(params[:issue]) else @issue = Issue.find(params[:id]) @issue.db_status = @issue.status end end def update @issue = Issue.find(params[:id]) if @issue.update_attributes(params[:issue]) flash[:notice] = '課題は正常に更新されました。' redirect_to :action => 'show', :id => @issue else render :action => 'edit' end end def destroy Issue.find(params[:id]).destroy redirect_to :action => 'list' end end
require File.dirname(__FILE__) + '/../test_helper' require 'kanri_controller' # Re-raise errors caught by the controller. class KanriController; def rescue_action(e) raise e end; end class KanriControllerTest < Test::Unit::TestCase fixtures :issues def setup @controller = KanriController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new @request.session[:login_user] = {:uid => 'gomi'} end def test_new get :new assert_response :success assert_template 'new' assert_not_nil assigns['issue'] assigns(:issue).title = 'aa' assert_equal assigns(:issue).next_statuses, ['未着手','回答待','対応中','リリース待','完了'] assigns(:issue).next_statuses.each do |next_status| assigns(:issue).status = next_status post :create , :issue => {:title => assigns(:issue).title, :status => next_status, :next_check_date=>Time.now} assert_response :redirect assert_redirected_to :action => 'list' end end def test_edit #get :edit, :id => @first_id assert true end end
ビューその1 new.rhtml
<h2>課題の登録</h2> <% form_tag :action => 'create' do %> <%= render :partial => 'form' %> <%= submit_tag "作成" %> <% end %> <%= link_to '戻る', :action => 'list' %>
ビューその2 _form.rhtml
<%= error_messages_for 'issue' %> <% @issue.next_statuses.each do |status|%> <%=link_to_status status%> <% end %> <!--[form:issue]--> <p><label for="issue_title">タイトル</label><br/> <%= text_field 'issue', 'title' %></p> <p><label for="issue_category">カテゴリ</label><br/> <%= text_field 'issue', 'category' %></p> <p><label for="issue_priority">優先度</label><br/> <%= text_field 'issue', 'priority' %></p> <p><label for="issue_description">説明</label><br/> <%= text_area 'issue', 'description' %></p> <p><label for="issue_next_check_date">次回チェック日</label><br/> <%= datetime_select 'issue', 'next_check_date' %></p> <p><label for="issue_status">ステータス</label><br/> <%= select :issue, :status, Issue.statuses -%><br/></p> <%=hidden_field 'issue','id' %> <%=hidden_field 'issue','db_status' %> <!--[eoform:issue]-->
最後にヘルパー
module KanriHelper def link_to_status(status) link_to_function(status,"var f = document.forms[0] ;f.action = '#{@issue.id}'; f.issue_status.value='#{status}'; f.method = 'POST';f.submit()") end end
※テスト自体の勉強という意味もあるので、
普通そういう書き方しないでしょというのもあるし、
一部だけ試してみて、網羅性までは確認できてないとうのもあります
今回は、ある課題を初期作成したり、
編集したりするときに、次のどのステータスに変われるのかの例をだし、
そのステータスごとに設定のお助け機能をつけようというもの。
まだお助け機能はほとんどできていないですが、今のステータスを判別して、
次にどのステータスに変われるのかはうまく動いているようです。
次は年明けに集まる予定。
状態遷移のテストを完成させて、次のステップに行きたいです。