2010-03-08 [長年日記]
_ emacs -nw では今のところ Zen-Coding は動かないらしい
Zen-Coding という名前だけは知ってはいたけど、実際に動くところを見たら便利そうだったので Emacs で使えるようにしてみた。
この辺りを参考にインストールしてみたけど、なーんでか動かなかった。よくよく読むと -nw では使えないってコメントらしきものが上がっている。そうか、すぐにはどうにもならないか。
ということで .emacs で
(cond (window-system ())
(require 'zencoding-mode))
して組み込むことにした。そのうち -nw でも動くかもしれないけど、とりあえずいいや。
2010-03-13 [長年日記]
_ TDD BootCamp 北陸1日目
※ 2日目もあります
イベントに行ってきた
去年の年末にあったTDD読書会&ふりかえり(実はその日記書いてない;)からの流れも含めて、なんと!
あの! t_wada が!
とか書いておくと follower がやってきてくれるかもしれないので名前出しを先にやってしまうけど、
に参加してきました。トラックバックセンターは
TDD Boot Camp 北陸に登壇させていただきました - t-wadaの日記
かな?
会場
白山里のウェブサイト/白山麓の自然に囲まれた天然温泉の宿、体験・研修交流館
で、分かる人は「バードハミング鳥越を越えて花ゆうゆうのもうちょい先」で分かります。ものすごい山奥を想像してたけど、地元民からすればある程度は想定の範囲内。ただし電波的にはかなり孤島で、
| docomo, au | OK |
| SoftBank, WILLCOM, e-mobile | NG |
です。施設の回線も弱く、イベント参加者の tweets が一部の人に偏っていたのはそういう理由からです。
小松駅集合
イベント自体には何も手を出していなかったので、送迎と酒の準備だけしました。朝に高速乗ったらどしゃ降りで前なんにも見えなくなって焦ったけど、洗車しなくてよかったなとか違うことを思ったりしていた。
小松駅で目印のために MacBook を広げてお迎え。なぜかこの瞬間、カバンの紐をまとめていたゴムがちぎれてしまった。まぁもう12年目だもんな、これ。
誰一人面識もないうえに場所もよく知らなかったけど、なんとかなるもんです。Google と Twitter があればボクらはもう平気。なのかもしれない。
内容
- レクチャー
- Workshop 1
- Workshop 2
レクチャー
t_wada 節の初歩*1。
現代の開発において
- バージョン管理
- テスティング
- 自動化
以上は
三本柱である。
「三種の神器では切実さが足りない」とも仰ってまして、あーなるほどなぁと。
三本あって初めて自立できる
のだな。
以降は個人的に刺さった部分だけ箇条書きメモ。
- バグの発見は早ければ早いほどいい
- (最近、自分はこの言葉を自明として端折る傾向がある)
- テストパス率を計るとか、そういうんじゃないんだ。一つずつ進むんだ。
- TDD の「黄金の回転」
- (JOJO成分が足りなかった。一瞬分からなかった。)
- テスト手法の観点から「書くべきテスト」を挙げることはできるのだが、TDDのテストはまず「不安」の部分をテストすることが大事
- スピリチュアルプログラミング(笑)
- 祈ることでなく確かめることが大事
- 「デバッガではダメです。再現できないから。」
- 書籍売り込みモード(違
- Fragile Tests (一部の変更でドガーンとコケるテスト)
- 内部構造に依存しちゃダメ。(同じことがプロダクトコードの結合にも言えるな。)
- FakeIt
- テストコード自体の間違いを生まないために、無駄っぽいテストを軽視しちゃダメ
- (テストファーストに徹しきれていないので、ときどきポカをやっちゃうんだよな)
- 三角測量
- GREEN のテストにテストを足して RED になることを確認して、コードを書く
- Obvious Implementation
- (「えいや」)
- TDDに才能は必要ない
- 「オレのは『技術(ワザ)』だ」
小さい単位
小さい単位に分割すること自体はスキルであり、慣れが必要であるという点が個人的には実はとても共感できるポイントだった。自分のコードが testable になってきたと感じられるまでに恐らく1年くらいは掛かっているから。
ちなみに読めるものは以下のものが該当するので、もしかしたら2年掛かっているかもしれません。
テストの書き方
assertEquals( expect, actual )
実は、この書き方、今まで逆にしていた。確認したい動作を先に書いて、期待する結果を後に書いていた。考え方次第なのかもしれないけど、
ゴールを先に書く
ということが大事だと、急に目が覚めた。反省した。
Feedback 重要
「決まっているものを変えない」「動いてるコードに触れるな」は No Feedbacks だよなと思った。そうか、そうだな、Feedback があることも安心感に繋がるんだ。
Workshop1 - 簡単なワードフィルタ実装
ワードフィルタの実装をペアプロ & TDD で行う。
Ruby で憧れの @ukstudio たんとペアだお。でへへ。と思ったけど、ここでいきなり宗教戦争勃発。Ruby というキーワードが同じだけで、Emacs vs Vim、日本語キーボード vs 英語キーボード、Test::Unit vs RSpec、もう何もかも違う。がーん、uk たんとは分かり合えないお。
ということで、あんまりよくないんだろうなと思いながら全面的に @ukstudio さんにおんぶにだっこ。rspec を丁寧に教えてもらう。
- rspec すげぇ! 絶対使う! レポート見やすい! pending 超ステキ!
- 普段テストの名前に頓着がないことが発覚
これまでも TDD ライクではあったんだけど、入り口が「とにかくユニットテストを行う」だったので、まずは網羅的にメソッドのテストを書くということしか意識できていなかった。単純に「test_目的のメソッド()」という形で作ってゴリゴリテストするというスタイルで、実際、プロダクトコードの読みやすさには気を使っていたけどテストコード自体はお世辞にも読みやすいとは言えない状態だった。
またこの形では確実にテストしにくいメソッドが出てくるのも気になっていた。普段は敢えて private などは作っていないのでテストできるが、でもこいつだけテストしても仕方ないよな、というメソッドは出てくる。このとき、単純にプロダクトのメソッドとテストメソッドの関係を 1:1 にするのではなく、意図とテストコードの関係を 1:1 にして記述できていればよいのではないかと思い至った。実際には他のペアを含めたペアプロ後の発表を聞いていて思ったんだけど。
この気づきは大きかった。すぐに活かすことはできないけれど、徐々にテストコード自体、あるいはテストの実行結果を設計書として読み下せる状態を目指したいと思う。
Workshop2 - 大変だ! 仕様変更だ!
ペアを変えて仕様変更に取り組む。
Ruby組4人のうち、@mitukiii と @wtnabe は両刀だったこと、PHP組が2人しかおらず、ペアの変更ができなかったこともあり、このセッションは PHP にスイッチした。
今度は逆に自分の方が TDD のスタイルに慣れていたのでいつもの SimpleTest + Terminal 上のテスト実行の形で引っ張る。Workshop1 の C# ペアだったかがやっていたのを真似して git に突っ込みながらやることに。
ただ、説明しながら書きながら思ったのは、やっぱり自分はテストファーストじゃないんだなぁって辺り。事情があって急いでいたのもあって、いつもの要領でプロダクトのメソッド2つ書いてテストメソッド2つ書いて、みたいなこともやってしまった。悪い例を見せてしまったかもしれない。ごめんなさい。
この仕様変更ではある程度何をどう実現するのかは自由だったので、ペアで設計を議論した。これもやりたいやりたいと思いながらも普段なかなかできなくなっていたことなので、とてもよかった。インターフェイスをどう決めるかによってステップの大きさが変わるケースだったので、より小さいステップになる方法を選ぼう、と言えた自分を誉めたい。そういう発想ができる程度にはイテレーティブにやれているんだなと振り返れた。
※ ところで、この仕様変更では「NG ワードを複数設定できるようにする」という変更があったのですが、他の組の発表で、filter そのものはいじらず、filter を複数に増やすという方法を採用しているところがあって、こーれはやられたなと思いました。
発表の際にちょっと勝手なことを言ったんだけど、もう一度まとめておこう。
- PHP は標準でテスティングフレームワークが付属していない
- 「Java にとっての eclipse」のような絶対的な環境がない
- (フルスタックフレームワークが個別にテストツールも持っている*2)
ため、「Terminal でテストを動かすのはあまりやらないんじゃないか」という旨のことを言った。これはやっているところが「ない」という意味じゃないんだけど、一般的な「PHPのコードを書いている人」にとってはテスティングフレームワーク自体があまり馴染みがないのかなということは感じている。
BootCamp 会場では Rubyist, Pythonista に比べて PHPer が少なかったわけだけど、いわゆる「現場」で PHPer の方が少ないっていうのはかなり考えにくいことで、その比率だけ見ても違和感はあるし、このセッションでペアを組んだ方も ZendFramework は使っているがテスティングフレームワークは使っていない様子だったし、これまで PHP の仕事を発注した経験からも、TDD というかユニットテストを揃えて早期に自動テストを回し始めるスタイルでやっているところはかなり少ないんじゃないか。他の言語での開発を発注したことはないので、PHP だけの特徴かどうかは分からないけど。
こうした話とはまた別な理由で PHP ではテストのやりにくさを感じているんだけど、これはまた後日書けたら書きます。
合宿 - 新たな「黄金の回転」の誕生
ご飯と自己紹介。仕事で GAE ! Python ! 何それ都会ってすげぇ!
持ち込みました。「白山」とふぐのぬか漬け。概ね好評でした。白山とぬか漬けの回転、ビールとビーバーの回転。回る回る。
自分はこの夜の部ではもう一切機械には触れず、酒を飲みながら @yuuitiro さんと酒談義したり、どうしても生で聞きたかった @ukstudio さんの職業プログラマ観を聞いたり、最後の方はなんか愚痴ったりしていました。おっさんにはきつい時間でしたが、とても楽しく、またためになる時間を過ごせました。ありがとうございます。
2日目に続くよ。
2010-03-14 [長年日記]
_ TDD BootCamp 北陸2日目
昨日の続き。
ハヤオキクラスタ数名がご飯の前にお風呂でウダウダ。ここでもちょっと愚痴ってしまう。いかんいかん。
ブレックファストセッション
朝ご飯を食べながら今日の方針をうだうだと。悩みは多いがやはりレガシーの方向に向かうことに。
ウェイクアップセッション - レガシー vs TDD デモ
報告。なぜか急に腹が緩くなった。
Ioke という JavaVM 上の言語へのコマンドを Twitter から投げられる bot のレガシーコードにテストを付加し、機能追加を考えながら、手法の解説を行うという、かなり @t_wada 頼みのセッション。
検体の活きが良すぎて若干消化不良ながらも以下の解説が繰り広げられる。
- まずバージョン管理、そしてブランチを切る
- 現在の動作を Characterization Test で浮き彫りに
- 密結合の class をぶった切る。コンパイラに怒られるかどうかでメドをつけていく。ここはコンパイラと IDE の力に任せる。
- 「gitk だけが友達です」
- (やはり eclipse のコードアシスト(?) は便利。Emacs にも似た機能があるっぽいんだけど、聞き逃してしまった。)
- sensing variables
- (あとで捨てる前提の確認用メンバ変数。あるいは Logger なども含めて、内部の動作を明らかにする工夫。)
gitk 知らなかった。便利。MacPorts で入れたもの(たぶん)はフツーに使える。 Fink で入れたものはなんかフォントがずれてる。
正攻法で攻め切れないので迂回路を仕込んで動く状態に持っていくような感じ。「追い込み漁」という言葉が浮かんだ。
頑張って追いこんでいったけど、最終的に利用しているライブラリのバージョンが古くて動かないという事態に。
このセッションで最も印象的だった言葉を書き忘れていた。それは
「分かりました。引き返しましょう。」
道を分け入って分け入って、ここから先は危険と判断して別なルートから道を切り開こうとする探検家のような、そんなイメージが鮮明に思い浮かんだ。これは普段から自分がそういうイメージでコードと戦っているからなんだと思うけど、「同じ言葉を使っているんだなぁ」と嬉しくなった次第。
レガシーのレガシーたる所以
仕様変更に気づけないことがあるという話。テストが揃って CI で回していれば落ちた時点で気づける。
※ 実は今の自分の悩みはここ。CI まで手が回らない。自分がコードを書く時間を減らさないと、もう他のことまでできないんだよなー。
アフタヌーンセッション - レガシー vs TDD Coding Dojo
Coding Dojo については [記事紹介] Coding Dojo: InfoQ 〜 TDDを根づかせる:導入の問題と解決策: TDD.NET 参照のこと。一言で言うと1台のPCでペアを次々に取っかえひっかえしていくペアプロの乱取りです。
ここでは
wtnabe's pukiassist at master - GitHub
を検体として提供して Ruby でレガシーと闘うチームに参加。6人のうち Rubyist 2人という状況を見かねて @t_wada が参戦! これで勝つる!
まずはコードの目的と動作を説明。いきなり動かなくていろいろ焦るが、どうにか動く状態に持っていったところで、さて何をどうしようか。
ここで鶴の一声!
@t_wada 「パーサがありますね! パーサいいですね! パーサにしましょう!」
そうです、このツールには PukiWiki の記法を setext 記法に変換する機能が含まれているのですが、こいつを料理しようという展開。確かに、自分でも TDD のサンプルコードとして文字列処理はとても良いと思う。後から考えてもこのパーサに絞った判断は絶妙だった。*1
- まずは rspec の雛形を準備してもらう。インスタンス化できない心配はここではないので、順調に進む。
- しかしどのようなパラメータを与えればよいのか分からず。コード書いた経験からいちばん下に if ( __FILE__ == $0 ) で実行する処理があったはずということでそこを確認してもらう
- テストデータを食わせるといきなり例外で死ぬ
すわ。ここが改善のポイントでは!
- どこで例外投げてるの?
- おぉ、例外を投げ直している!
- 作者コメント。えーとたぶんこれは見出しを変換する convert_heading() で投げてます。
- まずは convert_heading() の characterization test 書きたいんすけど
- 素晴らしい。それで。
- あー h3 相当の行は変換できないんですわ
- じゃあテストデータからとりあえず外して動くようにしましょうか
- 動きがつかめてきた
- じゃあ h1, h2 以外は受け付けない仕様になっているので h3 のときのテストに入りましょう
- 動かないっすねぇ。何がいけないんだろう。
- この if 文の動きを分けてみたいですね
- なるほどここでこの値以外は無視してるので無視しないように case ( when ) を追加しましょう
- よし、期待通りの case ( when ) を通るようになった
- じゃあ、h3 のときどうして欲しいか設計しましょう
- あとは目的の動作をするまでプロダクトコードをいじる
だいぶ端折ったけどこんな感じの流れ。
コードの中の問題箇所の同定については書いた本人がいたのでたぶん「現場」のレガシーコードを相手にしたときよりずいぶん速かったと思う。
自分一人でやったとしても流れとしては似たような感じになったと思うけど、
いま何をしているのか、t_wada の解説付きで一つずつ作業が進む
という贅沢仕立て。これで頭と身体に入ってこなければ嘘。
※ しかし冷静に考えるとわずか1年前のコードなのになんでテストがないのかな。あー PukiWiki -> setext 変換はもっと前に書いてて、他の部分も外部のツール(PukiWiki, smtpd)に完全に依存しているので、テストは諦めたんだ。個人的にはこういうツールは今後もよく作りそうなので、mock 力は必要だなー。
まとめ
- とにかくペアプロは疲れる。二人同時に気が抜ける瞬間はまず来ないので生産性が落ちない。
- オフライン用の道具大事。chm ファイル、IDE、ri, refe, irb、「本」など。
- でもたまのオフラインはいいかもしれない
- LL使いは「標準」の環境がないので、若干ペアプロのハードルが高いかもしれない
- TDDという言葉よりも、その中の手法や概念を説明する言葉、そして何より経験が自分の理解と実感を確かにする
- 2日目は JavaScript 経験率100% ! もしかしたら JS + jsUnit が TDD 勉強会では最強か? *2
- 丁寧に「デバッグ」
昨日の分も
- Emacs 以外の人にも優しい環境を目指す
- 一応もう MacVim は動いてるよ!
- テストの意図がストレートに表れるテストコードを目指したい
- rspec がこの部分ですごく便利。テストケース一つ一つはメソッドではないので、名前付けが自由に行えるし、記述も読み下しやすい。レポートが見やすいし、わざと RED にしたりコメントアウトしたりしなくても pending だけで「飛ばせる」。
- 自分の三角測量がちょっと疎かだった
- やはりペア設計、ペアプロ、ペアデバッグはハマりを減らしてくれる
- バージョン管理の安心感重要
コードはまた今後。
最後にスタッフの皆さん、出席者の皆さん、そして @t_wada ありがとうございます! やっぱ hands-on はいいですね。またやりましょう!
2010-03-15 [長年日記]
_ TDD BootCamp Hokuriku Extra Day - Development Antinews
これは個人的なイベントというか、起きたこととそれの観察記。
そして、BootCamp と日常を重ねて思ったことなど。
大変だ! レガシーコードの修正だ!
TDDBC の翌日は普通に仕事していたのですが、この現場には Camp とは比較にならない劣悪なレガシーコードがゴロゴロしています。
そこに修正の依頼が入りました。自分にじゃないですが。電話口のやりとりを小耳に挟んだので、要求を再確認。
- 対象はいわゆるMVCフレームワークを利用したWebアプリ
- 最速でやってくれ(ここがもう「なんだかな」ではあるんだが)
- 変更を加える必要のあるメソッドはもう見つかっているが、これが一つで140行あってベッタベタ
- できるだけここはいじりたくない
いやぁ、レガシーだねぇ。
- どうやら基本的にはブラックリストの考え方で迂回する方法で実現できそうだ
- なんか censor のない Camp ネタのように見えるな
- この部分だけ切り出せば圧倒的にテストしやすくなるぜ。ボリューム的にも TDD の導入にモッテコイダ
というわけで、判定処理の class を別個に作るように指示。
実録! これがレガシーな現場だ!
1時間後。
- 判定処理は class に分かれていた
- できた class は実際の処理の中に思いっきり組み込まれ、
- ブラウザで一回一回実行して人間が特定のボタンを一生懸命押して
- printfデバッグ!
orz orz orz
いや、まぁ。まぁね。TDD でやれとは言わなかったけどね。やったことはあるけど身に付いていない方法を「最速」と言われている仕事で使えとはよー言いません。
- なぜ判定にミスるのか、一生懸命に目視しているが分からない!
- けっきょく脇で見ていた自分が原因を発見
何のことはない、単に改行コードが混ざってただけで、実はこれ、自分が Camp でハマったのと同じ(笑) すぐ使える Camp
なんとか動く状態になったので、一応対応完了となった。
レガシーコードにレガシーに対応する方法のここがダメ!
上の流れの中で自分が「こりゃダメだな」と思った点を挙げておく。
実システムは遅い!
まずいちばん気になるのは、フレームワークに載っけて目視チェックする方法は不必要なコードがたくさん動いているので遅い。おまけにブラウザも不必要な機能がたくさん動いているので遅い。つまり、
Feedback までの道のりが遠い。
これが自分が PHPer なのにコマンドラインでテストを回している最大の理由。最小のコードだけを動かして最短で Feedback を得るため、そして自分の手を Terminal から離さず、より良いリズムを作るための手法として自分はコマンドラインでテストを回している。
※ まぁ、自分の環境が劣悪なのもあるとは思いますが。
ブラウザは特に「値」の目視チェックに向かない
目視チェック自体の是非はともかく、sensitive な目視チェックの工程にブラウザは向いていないと思う。なぜなら Web ブラウザは HTML のレンダリング向けのツールであり、ホワイトスペースはいい具合に省略して表示してしまう。
もちろん PHP なら var_dump() などを使うことでクォート文字列が出力されるので、「クォート文字の間に何があるか」を人間が一生懸命読めばチェックは可能。しかし。しかし、次に続く。
目視チェックの精度は決して上がらない
ここまでで、すでに
- ブラウザで正しいページを出して
- 正しい操作をトレースして
- その結果を人間が正しく読み取る
という、極めて高度な操作を行っている。
上のどれをミスっても現象の把握、問題の発見を始めることはできない。
野球に例えるとこれは高度な連係プレーなのだ。野球に興味のある人なら連係プレーが本番で完璧に機能する確率が決して高くないことをよく知っていると思う。上のテストのための操作はそれと同じである。つまり、こうした手動テストは
わざわざテスト工程にミスを自分たちで入れているのと同じ
である。また、成功したとしても、ここまでですでに脳力を浪費している。問題を発見し、デバッグを行うために使える MP が、デバッグ開始時にすでに減っているのだ。しかもこれを何回もくり返していればどんどん MP は減っていく。
疲労である。
上の方法では自動化されたテストよりも人間の疲労が大きい。結果、修正作業の速度は遅くなる。
断言する。遅くなる。
コメントアウト地獄
これは BootCamp のときにも感じたことなんだけど、日頃から TDD に親しんでいる人に比べてそうでない人の方が
コメントアウトを乱発する
傾向にあるように思う。このコメントアウトがまたコードの視認性を悪くする。
テストコードがない目視チェックの場合、当然、プロダクトコードの中でチェック用のコードがコメントアウトされたりインされたりをくり返すので、TDD していない人は
コメントアウトが入っていて当たり前
と思っているように見える。
この傾向はよくないと思う。プロダクトコードの中には、必要なコードと必要なコメントと必要な余白以外ない方がよい。コメントアウトそのものを全面否定する気はないんだけど、やはり気にすべき情報は少なければ少ない方がよいと思う。人間の脳は優秀だから無意識のフィルタリングは十分有効に機能する。しかし、本当の目的以外に力を使っているという事実は変わらない。
JOJOネタの向こうを張って HxH のヒソカの台詞で言えば
「君の敗因は容量(メモリ)の無駄使い」
オレ repos のススメ
上の話とも絡むんだけど、TDD 体質の人の多くはバージョン管理システムやエディタの機能に依存してしまうことの楽さをすでに知っていて、
「不要だよな」と思った瞬間コードをばっさり削っている
と感じた。これは
あとで必要になったら戻せばいいし、戻せる
ことを「身体が」覚えているからなんだろうなと思う。
ここで言うバージョン管理システムは、より柔軟な運用ができる分散バージョン管理システムのことを指すと考えてしまった方がよいように思う。自分も去年から official な repository である svn とは別に、自分用に必ず git か hg を併用するようにしている。この方法は多少の混乱は生むけど、
svn の commit をきれいに保ちつつオレ repos で好きなようにコードを蹂躙できる
ので
「自分の作業に対する不安」が減る
ことに繋がり、とてもよいと思う。
CVS や svn 一本だと、例え branch でもあまり変な commit を作るわけにいかないというプレッシャーが残ってしまう。このプレッシャーをオレ repos で解消すると、よりコード変更に自信を持って取り組むことができる。目先の面倒が多少あっても、最終的に
自信を持って取り組めるかどうか
が、いちばん大事なんじゃないかと最近よく思う。
当たり前の話だけど不安やプレッシャーは能力の発揮を妨げる。これはアスリートたちだけの問題じゃない。我々の日常の小さな作業においても同様に、不安やプレッシャーを取り除くことが大事なんだ。
以下完全に余談。
「git は速いから」という理由を挙げている記事を目にするけど、必ずしもすべての操作において git の方が svn より速いわけではないことには注意しておいた方がいいと思う。特にリポジトリがでかいとかディスクが遅い(ネットワーク越しとか)ケースでは、branch の操作は git の方が速いけど日常的な diff 出しは svn の方が速い、ということも起きる。日常的な操作が遅いとリズムが悪くなってしまう。
2010-03-16 [長年日記]
_ Emacsでカナ文字の全角半角、ひらがなカタカナ変換ができることを知った
先日の TDDBC 北陸の2日目のデモセッション、さも真面目に話を聞いていたかのようにレポートを書いたけれども、さすがに途中で意識が飛びそうになること幾度、しかしネットワークはこのデモセッションに占有されているので普段の感覚では内職はできない。
そうだ、Emacs のコマンドを片っ端から調べよう、と思って見つけたのがこれ。
japanese-hankaku-region japanese-hiragana-region japanese-katakana-region japanese-zenkaku-region
なんてこった。Emacs だけで半角カナが入力できる。普段半角カナを入力できないようにしてあるので、どうしても入力しなければいけないシーンでとても困っていたのだけれど、Emacs だけで対応できることが分かった。
あーなんて有意義な TDDBC (違
もしかして CarbonEmacs だから入っている elisp かもと思ったけど、そうでもないみたい。CentOS 5 の Emacs 21.4 でも動くから意外に古くからあるらしい。全然知らなかったなー。
あと M-x ipconfig すると ifconfig できるし、man も引けるのか。知らない機能がいっぱいあるなぁ。
2010-03-22 [長年日記]
_ Cucumber を PHP アプリに対して本当に使えることが分かった
何がきっかけかよく覚えていないんだけど、この数日、急に Cucumber のことを思い出したので試してみた。
Cucumber は一部ではとても有名な受け入れテストの記述&実行フレームワークとでも呼んだらいいのかな。有名な記事はこの辺。
- Cucumberがアツい - moroの日記
- UK STUDIO - Cucumberの登場でRailsのテスティング環境が変わった
- Ruby Freaks Lounge:第21回 Railsアプリの受け入れテストをCucumberで書こう|gihyo.jp … 技術評論社
まだあまり詳しく本家ドキュメントを読んでないんだけど、あちこちの記事から分かることは、これは
外部仕様あるいは要件定義を共有しやすくするフレームワーク
ということでしょうか。あたかも自然言語で書いたプレーンテキストがテストを動かすスクリプトとして機能しているように見えるので、今の動作で仕様を満たしています、あるいはここまでできています、ということを開発者間、開発者とマネージャ、プロダクトオーナーと共有しやすくなることが嬉しいみたい。
なんのこっちゃって感じですか?
機能的な部分をごく大雑把に言い直すとテスト用の DSL を提供して、そこからあれこれテストツールを呼ぶよ、って感じです。こっちの方が具体的な動作はイメージしやすいかな。
あとは
examples at master from aslakhellesoy's cucumber - GitHub
ここら辺のコードを眺めて動かしてみるとつかめると思う。
もちろん有名なのは Rails のテストなんだけど、例えば Ruby の Test::Unit と連携することもできるし、Webrat というツールを使うことで PHP アプリをテストすることも可能。
PHP アプリのサンプルがなかったので探したみたら
Cucumber+WebratでPHPアプリのテストをする | CAPH TECH
が見つかった。ただしどうもちょっと古いバージョンをもとにしているらしく、0.6.3 では動かなかった。Ruby 1.8.7 + Cucumber 0.6.3 で動かすにはこんな感じで修正が必要。
diff --git a/cucumber.yml b/cucumber.yml
index c39f416..c1869b5 100644
--- a/cucumber.yml
+++ b/cucumber.yml
@@ -1 +1 @@
-default: --language ja features
+default: features
diff --git a/features/show.feature b/features/show.feature
index e3afa77..3887cb7 100644
--- a/features/show.feature
+++ b/features/show.feature
@@ -1,3 +1,4 @@
+# language: ja
機能: トップページの表示とサブミット
シナリオ: トップページの表示
diff --git a/features/step_definitions/result_steps.rb b/features/step_definitions/result_steps.rb
index b22e762..85c2824 100644
--- a/features/step_definitions/result_steps.rb
+++ b/features/step_definitions/result_steps.rb
@@ -1,11 +1,11 @@
# -*- encoding: UTF-8 -*-
ならば /^"(.*)"と表示される$/ do |text|
- response_body.to_s.force_encoding("UTF-8").should =~ /#{text}/m
+ response_body.to_s.should =~ /#{text}/m
end
ならば /^"(.*)"と表示されない$/ do |text|
- response_body.to_s.force_encoding("UTF-8").should_not =~ /#{text}/m
+ response_body.to_s.should_not =~ /#{text}/m
end
ならば /^(\w+)メッセージが表示さる$/ do |message_type|
diff --git a/features/support/env.rb b/features/support/env.rb
index 54a9528..87f3521 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -1,5 +1,6 @@
# RSpec
require 'spec/expectations'
+require 'cucumber/formatter/unicode'
# Webrat
require 'webrat'
diff --git a/index.php b/index.php
index 5623672..bac22ea 100644
--- a/index.php
+++ b/index.php
@@ -9,7 +9,7 @@
<?php if(!isset($_GET['text']) or empty($_GET['text'])) print '<p>テキストを入力してください。</p>'; ?>
- <form action="/php_with_cucumber/index.php" method="get">
+ <form method="get">
<p><textarea name="text" rows="10" cols="100"></textarea></p>
<p><input type="submit" value="Submit" /></p>
</form>
簡単に言うと
cucumber-0.5.0へ上げるときの注意点 - ヽ( ・∀・)ノくまくまー(2009-12-21)
を読めって話になります。読め。ただし、
require 'cucumber/formatter/unicode'
はちょっとした罠かも。example を読んでれば気づくけど。
あと以下の部分でテストするアプリの URL を決め打ちしてるので、対応が必要。
features/step_definitions/webrat_steps.rb
# -*- encoding: UTF-8 -*-
前提 /^(.*)ページを表示している$/ do |path|
@response = visit "http://localhost/php_with_cucumber#{path}"
end
もし /^(.*)ページを表示する$/ do |path|
@response = visit "http://localhost/php_with_cucumber#{path}"
end
もし /^"(.*)"ボタンをクリックする$/ do |button|
こういうのちょっとダサイよねぇ。開発環境、検証環境、本番環境の違いをもうちょっとスマートに書き分けたり、開発環境の URI が増えていっても対応が楽になるような工夫がないものかしら。*1
感想
Webrat って Mechanize と何が違うの? と思ったら内部に Mechanize 用の adapter があって、実行時のレポートで Mechanize の警告が出たので、結局実際に外部のアプリにアクセスする部分は Mechanize を利用しているらしい。ここら辺、依存が深くなってきてちょっとややこしい。
また、Rails には plugin があるので Cucumber は使い始めやすいと思うんだけど、素の状態から使うのはちょっと面倒かなって感じがした。example レベルに留まっている step の記述がもっと充実してくれば違うんだろうけど、今のままだと
- プロダクトコード
- テストストーリー
- ストーリーを記述するための step
を書かなきゃいけないし、step はどうしても Ruby で書かざるを得ないのでプロダクトコードを別な言語で書いている場合はかなり面倒なことになってしまう。
ただし、その面倒をあえてやるべき場面もあって、それは内部の作り替えのとき。Cucumber で外側のテストがしっかり動かせていれば中の設計、もっと言えば中の環境はどうでもいいと言える。レガシーコードに悩んでいるアプリも、外を固めて差し替えてしまうことができるようになる。個人的には Mechanize はだいぶ慣れてきてるので、うまく使えれば動作の変化を最小限に抑えつつ古いコードを捨てるために使えるんじゃないかと考えている。
※ 学習曲線の初期段階においてにはちと荷が重い取り組みなので、試しに採用できそうな適切なサイズのアプリの見極めが大事になってきそう。自分の手もそれに集中させるのは難しいだろうし、古いアプリの仕様を後から書かなきゃいけないのも大変。現場は常に理想の正反対を行っているのだなぁ。でもそれがあるから新しいアイディアを思いつき、新しいツールが生まれてくるんだろうけど。
*1 そもそも本番環境を Cucumber でテストするっていう考え方がおかしいかもしれなので、必要ないかも。
2010-03-23 [長年日記]
_ URI encode/decode
違いがあるのは分かっていたけど、今までぼんやりしていた JavaScript と組み合わせる際の注意点がちょっと分かった気がする。
要は \x20 の空白と + の扱いの話で、
- JavaScript の decode では + -> \x20 変換はしない
- JavaScript に渡す際は \x20 -> %20 変換を行う
ということかな。組み合わせ的には以下で正しく動いている様子。
| PHP | rawurlencode() |
| JavaScript | decodeURIComponent() |
今回は逆方向は試してないけど、恐らく同じ扱いでいいと思う。
2010-03-30 [長年日記]
_ Tracのチケットに時間を入れる
目前の課題はともかく、今後のことを考えるとやっぱ時間の計測は必要だなと思ったので、以下を参考にとりあえず数字を入れる場所を用意することにした。
TracTicketsCustomFields – The Trac Project
入れるだけなら以下のような記述を conf/trac.ini に書けばイケる。
[ticket-custom] time_actual = text time_actual.format = text time_actual.label = 消費時間(h) time_expect = text time_expect.format = text time_expect.label = 予想時間(h)
これで ticket の末尾に予想時間、消費時間がいい感じに追加される。期待通りに「予想時間」の方が「消費時間」よりも前に出てくる感じになるんだけど、なぜかは分からない。単純に key の辞書順に並んでるなら消費時間の方が前に出てきそうなもんなんだけど。
試しに ticket を作ったら ticket_custom という table に以下のようなデータが作られた。
select * from ticket_custom;
| ticket | name | value |
| 111 | time_expect | 1 |
| 111 | time_actual |
※ ここで 111 は ticket ID, 1 は予想時間を1時間と入力した値。
ははぁ、なるほど。
View Tickets の query を書き直さないと実際には嬉しくないかもしれない。一応やってみたけど、これ全部の query に反映させるのはホネだし、なんかうまいこと設定ファイルかコマンドラインで変更できるのかなぁ?
SELECT p.value AS __color__, milestone||' Release' AS __group__, id AS ticket, summary, component, version, t.type AS type, owner, status, time AS created, changetime AS _changetime, description AS _description, - reporter AS _reporter + reporter AS _reporter, + e.value as expected, + a.value as actual FROM ticket t, enum p + LEFT OUTER JOIN ticket_custom e ON (t.id = e.ticket AND (e.name = 'time_expect')) + LEFT OUTER JOIN ticket_custom a ON (t.id = a.ticket AND (a.name = 'time_actual')) WHERE status <> 'closed' AND p.name = t.priority AND p.type = 'priority' ORDER BY (milestone IS NULL),milestone, p.value, time
なんか。なんか気持ちよくない。SQLも得意じゃないので本当にこうなの?っていうすごくモヤモヤした感じ。
_ kumonopanya [ 書籍売り込みモード(違 <= わからない Implemantation => Implementation ..]
_ wtnabe [コメントありがとうございます。書籍の紹介はが続いた部分は正直ちょっとあまり覚えていないのです(笑) 書籍メモはたぶん..]