Ruby on Rails: The RSpec Book 読破記録: 15章から18章

■15章ではRSpecを快適に使うためのTipsが書かれています。

RSpec実行のオプションやエディタ、オートメーション化、Rake、RCovなどが紹介されています。

私はすでに紹介されているいくつかを開発環境に導入済みです。Ruby on Railsを使っているのでRake、それからrspec_railsRCovはよく知っています。Autotestは使っていませんが、同様の機能をGuardで実装しています。またエディタはTextMateの評判がいいのですが、LinuxなのでGeditプラグインをいくつか入れて使っています。Redcarも使ってみたのですが、日本語がどうも、、私には合いませんでした。

■16章ではRSpecをカスタマイズして使う方法が紹介されています。

・実行するサンプルとしないサンプルを分ける。
・モジュールを追加する
・before,after,aroundのグローバルフック
・14章でもすこし説明されていたMockの選択
・カスタムマッチャ
・カスタムフオーマッタ

などです。中でもカスタムマッチャはよく使いそうです。

RSpecをカスタマイズするのはもう少しデフォルトの状態に慣れてからでもよさそうです。デフォルトの状態でも十分に高機能だと感じていますし、現段階でわからないことがあってもいいかなぁと思います。さらに読み進めます。

■17章は「Cucumberの紹介」とあります。

Cucumber= Cucumberフィーチャー + Cucumberコマンド + ステップ定義
Cucumberフィーチャー = タイトル + 自由形式のナラティブ + いくつかのシナリオ
シナリオ = 任意の数のステップ

いくつかのポイントが説明されています。
・フィーチャのタイトルは短く簡潔に
・ナラティブはConnextraフォーマットを用いて記述しましょう
As a <role>
I want <feature>
So that <business value>
↓ 日本語で書くと
<だれだれ>が
<なになに>したいので
<なぜなら>これこれこういうことをしたいからだ
のようになります。うーん。。

Cucumberのファイルの頭に
#language: ja
と記述するとCucumberが日本語でも記述できます。もともとが英語なのでどうしても不自然ではありますがそれでも日本語で記述できるのはありがたいことです。対応する日本語訳は次のようにするとヘルプを見ることができます。
cucumber –i18n ja

・シナリオは成功するシナリオ(ハッピーパス)から書き始めて、いくつかの特異なケースを追加していきましょう。
・シナリオに対し、ステップを追加していきます。そのとき
Given、When、Then、And、But
で始まるステップキーワードを用います。日本語だと
前提、もし、ならば、かつ、しかし
となります。

・この時点でcucumberを実行するとステップのコードが出力されるのでそれを利用して、ステップファイルを作成しますが、それについては後で説明するとのことです。
・ケースバイケースでステップを一つ一つ細かく書いたり(命令型)、ざっくり書いたり(宣言型)しましょう。
・シナリオが多い場合はfeaturesディレクトリの中に任意のフォルダに分けてfeaturesファイルを整理しましょう。
・タグを活用しましょう
シナリオにタグをつけると後で、そのシナリオだけ実行したり、させなかったりできます。

■18章ではCucumberについてのより詳しい説明がされます。
シナリオに定義されているステップの中で、ステップの内容が定義されていないと、cucumberを実行した際に定義すべきコードが出力されますので、それをfeatures/step_defenitions/の中の任意のファイルに定義します。定義は正規表現になっており、キャプチャグループが引数として扱われます。正規表現そのものを書くことは少ないですが、すこしでも使ったことがないと理解が難しいかもしれません。

使ったことがない人へのヒントとしては「/」で始まって「/」で終わる。「/^」が文頭で「$/」が文末。「([^”]*)」というのは簡単にいう「なんらかの文字」という意味です。この()カッコでくくられたものがキャプチャグループです。それでそこにマッチした文字列が引数となりますよ。と。

RSpecと同じようにフックもあって、タグごとに別のフックを付けられたりすることも説明されています。
カスタマイズはWorldオブジェクトを使います。

ステップ定義の中で別のステップを呼び出せる説明されています。たとえばさまざまなログインをまとめて定義したいとき。

When /^I am logged in as admin$/ do
When I am logged in as “admin”
end
When /^I am logged in as “([^”]*)”$/ do |user_role|
end

とするといいらしいです。別のステップを呼び出すときにそのステップに引数を渡す場合「”」はエスケープしましょう。

When /^I am logged in as “([^”]*)”$/ do |user_role|
When “The email is \”#{user_role}\”@example.com”
end

ステップ内のテーブルを用いて、まとめて複数のテストをしたり、初期設定であらかじめデータを作っておくようなテストをしたりできます。このことははじめのほうの章で簡単なアプリケーションを作ったときに体験しました。

17章と18章はCucumberのざっとした概要になっており、前の章で簡単なアプリケーションを作成するときに使ったテクニックのおさらい+もうちょっと突っ込んだ内容になっています。Cucumberのリファレンスがあればそれを読んだほうがより詳しく知れますし、RSpec同様読んだからといってマスターするまでは至らない程度のざっくり感です。

19章はいよいよRailsではどのようにすればよいのかという一番知りたかった内容が書かれています。

Cucumber1.1.9, capybara1.1.2で日本語を使用するとき便利なステップの定義

たとえばadminしか新規ユーザーを作成できないという仕様で、adminの場合だけユーザー作成画面を表示できることをテストをしたい場合、結果をStep文に含めてしまうと便利なことがあります。

#language: ja

フィーチャ:新規ユーザーの作成
ユーザーとして
新規ユーザー作成画面を表示したい
roleによって新規作成できることを制限したい。

シナリオアウトライン:admin以上であればユーザー新規作成画面を表示できる。
前提 role”<role>”としてログインしている
かつ “ユーザー新規作成画面”へ行く
ならば “<message>”と表示され<boolean>はずだ

例:
| role | message | boolean |
| admin | CanCan::AccessDenied | ない |
| manager | CanCan::AccessDenied | |
| staff | CanCan::AccessDenied | |

上記の場合は表示され(る)か(ない)が入るので、ステップ定義で

ならば /^”([^”]*)”と表示されるはずだ$/ do |message|
page.should have_content(message)
end
ならば /^”([^”]*)”と表示されないはずだ$/ do |message|
page.should_not have_content(message)
end

とすると日本語に結果を含めることができてとてもわかりやすくテストが実装できます。

ちなみにユーザー関係の実装はDeviseを用い、roleによる権限はcancanを用いました。上記の例ではCanCan::AccessDeniedが表示されるかどうかをテストしていて、adminの場合は拒否されないので表示されないことが確認できます。

お客さまと話をするとよく出てくるIT業界へのクレームから学ぶ

お客さまと話をしているとIT系の人にたいするクレームが出てくることがあります。真摯に受け止め、うちではそのようなクレームがでないように努めたいです。

その1、ぼったくり。
これが一番おおいです。
・ドメインの更新料10万円とられた。
・サーバー代金初期費用100万円で月10万、後で知ったが格安レンタルサーバーだった。

などなど、そのような話をお客様から聞くと心が痛みます。 大きなプロジェクトであれば、単なる請求書の数値あわせである場合もあるかと思いますが、小さな会社さんのITに対する夢を打ち砕くような状況もよくあります。私の父の会社も使いもしないホームページとEメールアドレスの発行に莫大なお金を払っていたことを後で知りました。

IT業界は黎明期に新しい技術や用語を武器に、なんとなく景気がよかったですよね。その頃といまとを比べたらサーバーも高価だったり、技術者も情報量も少ないゆえの希少性だったり、そういうまっとうな理由がありますので、安易に「ぼったくっていた」とは言い切れないです。市場が成熟して競争がされると値段が安くなりますし、市場自体が広がっていますからそれだけで各サービスの値段は下がっていくのが自然です。お客さんのクレームの中にはその頃支払ったお金と、いま同じことを発注するのに支払うお金とを比べて「ぼったくられた」といっている場合もあります。ですが、そうやって大目にIT業界を擁護しても擁護しきれない現時進行形の「ぼったくり」にもよく遭遇します。

・昔の値段がそのままの場合
・担当者のキックバック代が上乗せされている場合
・見積もっている人がよくわかっていない場合
・悪意のある場合

ぼったくり = IT業界全体。 この印象を抱いている人は多いです。日本人特有の「やさしいお客さま」が光だとすれば、その影です。それに密接に関係するのが次のクレームです。

その2、難解すぎるヨコモジ。
つぎからつぎへと知らない単語が繰り返されると、よくわからないから任せてしまいたいと思うかもしれません。自分の頭がわるいといわれているようで腹が立つかもしれません。「サーバー」「ドメイン」などは代表的なIT用語といえますが、それでさえ普通に暮らしていたら知らなくてよい言葉です。次から次へと登場するIT用語ですが、お客さんが知らなくてはならない用語はひとつもないです。専門用語ですから、専門家がしっていればよい言葉です。そうは言ってもサービスの名前(ツイッターなど)や企業の名前(グーグルなど)は仕方ないです。外人だと思って覚えていただくしかありません。IT用語はものすごい勢いで増えていっていますので、現役で活躍している技術者でさえ覚えるのが大変です。非常に高度な専門性が要求される業界であるという意味では「~~士」と呼ばれる人々(たとえば弁護士)と同じような職種といえますが、一方では若い方を中心に「作り手よりもよく使いこなす人々」もいるという特徴もあります。わからない用語を並びたてられたら、遠慮なく「もっとわかりやすく言って」といってください。わかりやすくいえなければ、話している人自身がよくわかっていない可能性があります。

その3、すぐ「できない」という。
私もいってしまうときがあります。つい。
・予算的にムリ → ムリをすると食べていけなくなります。
・時間的にムリ → ムリをすると体を壊します。
・技術的にムリ → 自分じゃできないこともありますし、誰にもできないようなこともあります。
・倫理的にムリ → 法律にふれるようなことはできません。
・体制的にムリ → 毎日決まった時刻に手動で更新したいとしたら、ある程度の人員が必要です。

理由はさまざまですが、精一杯がんばっても困難なときがあります。

「できないできないってなんでできないの?」ITの仕事の多くはコードを書くことでしょう。そのコードは難解な数式のように見えます。コードが数千行になることは普通で、数千行あるコードが数千を超えるファイルになることも珍しくありません。それらのコードは一文字も間違ってはならず、足りなくてもいけません。そういう世界の話です。

一口にITといっても、内科・外科など医術の世界と同じように得意分野があります。具体的にこれがやりたいということがあれば、それを得意とする人を探してください。苦手とする人に頼むと、見積もりが非常に高い場合があります。もっとややこしいケースでは、低く見積もりすぎて後でトラブルになったりします。

発注会社を選択するコツはもっとあります。ホームページを作りたいというシンプルな要望であっても、それがもたらす目的や効果がご自分ではっきりとしていない限り、われわれが行うお仕事は「ホームページを作ること”だけ”」ではありません。ホームページは場所のようなものです。八百屋をつくるのと大規模商業施設をつくるのとは違うことは誰もが知っていることですが、「ホームページをつくる」という単純なオーダーについても同様な違いがあります。単純作業ではないのです。ですから制作会社が「ホームページ制作~万円」という料金体系にするのはわかりやすくすることが目的であって、本当は心苦しいはずです。

「載せる内容・広告戦略・機能」の三拍子をご自分で計画できる自信があるか、担当の方があなたのビジネスに関してとても親身になって考えてくれるか、あるいは事がそう難しくないか、そのいずれかでなければ不幸な結果になります。ある程度ビジネスが複雑で、ご自身ですべてを把握するのが難しく、お願いする担当者があなたのビジネスを単純に見すぎているとすれば、考え直す時です。

 

ビジネスとは別に、システムというややこしい要素がこの業界にはつきものです。システムは建築と似ています。構造的で、使うものであり、メンテナンスが必要で、いずれ作り変える必要があります。作って終わりではないのでシステムをつくった人間・企業とずっと付き合っていけそうかどうかは、好き嫌いも含めてとても重要なことです。

最後に、コンサルティングという罠。
専門性という意味でわれわれの仕事にも「~~士」と呼ばれる職種と同じような側面があるということを述べました。「~~士」というのはコンサルタントと似た部分がありますよね。私が日々お客さまと話をしていると似たようなことをしているなと感じることがありますし、お客さんからそういわれることもあります。「Facebookのコンサルティング」「クラウドのコンサルティング」「SEO」「アフェリエイト」など、難解な道具を専門的に教えてくれる人たちがいます。かれらはあなたのビジネスに自分の専門分野を役立てたいと考えています。しかしあなたのビジネスがそれだけで成り立つわけがありません。おいしい料理を作ることがあなたのビジネスだとすれば、彼らがいうことは「この包丁がいかに切れるか」「この鍋がいかにすばらしいか」といっているのと同じです。違うのはよく切れる包丁かどうかを知るのは簡単なのに、このヨコモジがどれほど役立つのかは難しいということです。ドブに捨ててもいいほどのお金と時間があるか、全面的に信頼して任せられる人がいる場合を除いては、あなた自身がそれらがどのくらい役立つかわかるまで、それらに手を出すべきではありません。

 

というわけで

・ぼったくらない
・わかりやすく話す
・できないといわない
・お客さまのビジネスを考える

ということを軸にこれからやっていきたいと思います。

 

Ruby on Rails Ver.3.2.0 DeviseのControllerをカスタマイズしたときにRSpecを書いてつまずいた記録

Deviseのコントローラをカスタマイズするためには、config/routes.rbを下記のように変更します。
devise_for :users, :controllers => {
:sessions => “users/sessions”,
:registrations => “users/registrations”,
:passwords => “users/passwords”
}

この例ではdeviseをuserとして使っている場合です。

この場合はapp/controllers/配下に新しくusersフォルダを作成しますが、app/controllers/配下に直にファイルを作成してもOK。
ちなみにそのばあいは:sessions => “sessions”とかになります。

また、session用、registrations用、passwords用と作っていますが、deviseに設定した機能によっても違いますし、もちろんカスタマイズの必要がなければ書かなければいいだけのことです。自分が使っているdeviseのcontrollerを知りたければrake routesで調べましょう。

app/controllers/users/sessions_controller.rb
app/controllers/users/registrations_controller.rb
app/controllers/users/passwords_controller.rb

を作成してそれぞれdeviseのクラスを継承します。sessions_controllerの場合は書きのような感じ。

class Users::SessionsController < Devise::SessionsController
end

app/controllers/直下にsessinos_controllerを作った場合は
class SessionsController < Devise::SessionsController
end
となります。

これであとはメソッドをオーバーライドするなり、before_filterをつかって処理を追加するなりすればいいです。
これで一通り動作することを確認。

ところがRSpecファイルでテストを書こうとすると、

Failure/Error: get(:create)
AbstractController::ActionNotFound:
Could not find devise mapping for path “/users/sign_in”.
Maybe you forgot to wrap your route inside the scope block? For example:

devise_scope :user do
match “/some/route” => “some_devise_controller”
end

みたいなエラーがでます。

下記のようにdeviseのmappingを指定する処理を各テストの前に実行すればよい模様。

before do
request.env[‘devise.mapping’] = Devise.mappings[:user]
end

エラーメッセージにあるようにconfig/routesのセッティングが間違っているのかと散々悩みました。

 

Ruby on Rails: The RSpec Book 読破記録: 14章から

■14章ではRSpec::Mocksについて説明されます。
あるオブジェクトがそれ単体で動作すればこれを使う必要はありません。通常はなにかほかのオブジェクトとのコラボレーションによって機能を果たします。しかし、それらのチェーンを追っていくと結局のところすべてのオブジェクトを作ってからようやくひとつのテストができあがるという事態になりかねません。事実私はRSpecを勉強しだした時に、Ruby on RailsのScaffoldを使ってかたっぱしからオブジェクトを作ってしましました。。オブジェクトをとりあえず作ること自体は合っていました。方法が間違っていて、scaffoldを使うべきではなく、RSpec::Mocksライブラリを使うべきだったのです。
●テストダブル←モック・スタブ・フェイク・スパイともよばれる。テストダブルといったときに、それが意味するものがいろいろであるため、混乱する。double,stub,mockメソッドは動作は同じで意味が違う。違いは後ほど説明するとのこと。
● メソッドスタブ ←とりあえず期待するレスポンスを返すようにするメソッド。
●メッセージエクスペクテーション ←一度も呼び出されないとエラーになる。
●メッセージエクスペクテーションは実際にアサーションであるが、アサーションされるのはコードが最後まで通過した後なので、Given Then Whenがわかりづらくなることが指摘され、それを解決するTest Spyパターンが存在することが紹介されています。ただ、私にはそれほどわかりづらいとは感じられず、むしろnot-a-mockやRRを使うほうが難解に思えました。たぶんこれらの説明が少ししかないからなので、後ほど調べてみようと思います。

●テストダブルを使うことによってデータベースから切り離してテストを行うことができると説明されています。Railsの場合はモデル、つまりActiveRecord=データベースのようなところがあるので、モデルのテストではテストダブルはそれほどつかわないだろうなと想像できます。逆にコントローラーではモデルのロジック(メソッドの内容)と切り離してテストがしたいので、これらを使わない手はないですね。

●ショートカット
customer = double(‘customer’)
customer.stub(:name).and_return(‘Bryan’)

customer = double(‘customer’, :name => ‘Bryan’)

複数でも指定できます。
customer = double(‘customer’)
customer.stub(:name).and_return(‘Bryan’)
customer.stub(:open_source_projects).and_return([Webrat’,’Rack::Test’])

costomer = double(‘customer’, :name => ‘Bryan’, :open_source_projects=> [Webrat’,’Rack::Test’])
※ここまで長いと逆にややこしいかも

●スタブチェーン
※ある執筆者が書いた新しい書物を取得する(RoR上のコード)
Article.recent.published.authored_by(params[:author_id])
↓スタブチェーンで実装すると
article = double()
Article.stub_chain(:recent, :published, :authored_by).and_return(article)
とする。オブジェクトが入れ子になっている場合に奥のほうのメソッドをスタブ化したい場合に便利。

●カウント(メッセージエクスペクテーションが呼び出される回数を指定する)
hoge.should_receive(:fuge).exactly(1).times #1回hoge.fuge()が呼ばれる
hoge.should_receive(:fuge).exactly(5).times #5回hoge.fuge()が呼ばれる
hoge.should_receive(:fuge).at_most(5).times #5回までhoge.fuge()が呼ばれる
hoge.should_receive(:fuge).at_least(5).times #5回以上hoge.fuge()が呼ばれる

1回の場合と、2回の場合はショートカットがある。
hoge.should_receive(:fuge).once #1回hoge.fuge()が呼ばれる
hoge.should_receive(:fuge).twice #2回hoge.fuge()が呼ばれる

●一回も呼ばれないことを期待する
hoge.should_not_receive(:fuge)
↓そのほか、あんまりつかわなそうな書き方
hoge.should_receive(:fuge).never
hoge.should_receive(:fuge).exactly(0).times

●この引数で呼ばれるはずだと期待する
hoge.should_receive(:fuge).with(50)
hoge.should_receive(:fuge).with(50, something)

●引数がこのインスタンスであることを期待する。
hoge.should_receive(:fuge).with(instance_of(Fixnum))
※通常はこのやりかたはオススメしないとのこと。そのサンプルの意図をはっきりさせるためであればつかってくれと。。しかし、どうしてオススメしないのかはかかれていません。たぶんテストで使われるデータには具体的な数値や文字などが使われる方が好ましく、そうであれば型をチェックする理由はないからなのかなぁと想像しています。

●なんらかの引数があることを期待する
hoge.should_receive(:fuge).with(anything())
hoge.should_receive(:fuge).with(anything(), 50)

●withがなければどんな引数でも受け取るけど、あえてそれを明示したい場合。
hoge.should_receive(:fuge).with(any_args())

●引数なしで呼ばれることを期待する
hoge.should_receive(:fuge).with(no_args())

●引数がハッシュのときに中身まで期待する
hoge.should_receive(:fuge).with(hash_including(‘A’=>1,’B’=>2))

●逆に引数がハッシュのときに中身にふくめてはいけないことを期待する
hoge.should_receive(:fuge).with(hash_not_including(‘A’=>1,’B’=>2))

●正規表現も使える
hoge.should_receive(:fuge).with(/.*User/)

●カスタムマッチャも使える※ある数よりも大きいことを期待するカスタムメソッドの例
hoge.should_receive(:fuge).with(custom_method(3))
#methodはクラスを返す
def custom_method
CustomClass.new(expected)
end
#classは「==(actual)」というメソッドを定義する。←そうすると後で呼ばれるらしい。
#description()メソッドを定義をしておけばエラーが起きたときのメッセージを指定できる
#initialize()メソッドを定義してクラス変数を保存すればより使いすくなる。
class CustomClass
def initialize(expected)
@expected = expected
end
def description
“#{@expected} より大きい”
end
def ==(actual)
actual > @expected
end
end

●連続して値を返すことを期待する ※本の中ではネットワークの接続を3回まで試すことを題材に説明しています。
hoge.should_receive(:fuge).and_return(nil,nil,nil)#hoge.fuge()はnilを3回返す
↓こうも書ける
hoge.should_receive(:fuge).exactly(3).times.and_return(nil)

●エラーが発生することを期待する
hoge.should_receive(:fuge).and_raise

●エラーの型を指定できる
hoge.should_receive(:fuge).and_raise(InsufficientFunds)

●エラーの型を指定したいが、そのエラーに引数がある場合は一度インスタンス化してそれを渡す。
instance = InsufficientFunds.new(:reason => :on_hold)
hoge.should_receive(:fuge).and_raise(instance)

●エラーではなくてシンボルを渡したいときはand_throw
hoge.should_receive(:fuge).and_throw(:insufficient_funds)

●正しい順序で呼ばれることを期待する
hoge.should_receive(:first_method).ordered
hoge.should_receive(:second_method).ordered
※逆に言うとorderedを付けないと順序は無視。
※ひとつのオブジェクトのみに有効。複数のオブジェクトに指定しても順序無視

●スタブは上書き可能
before do
@logger = double(‘log’)
@logger.stub(:log).and_return(nil)
end
it ‘test’ do
@logger.should_receive(:log).with(‘something text’)
end
※この使えばデフォルト値を設定できるのでコードを簡潔にできるときがある。 多用すると複雑になるかも。

●テストダブルの使いどころ
・まだ作ってないオブジェクトに
・ネットワーク、サーバー、ファイルシステム、などの外部システムに
・動作に時間がかかるオブジェクトに
・そのほか不確定になりすいオブジェクト

● モック作ると役割が見えてくることがある。状態ではなくやり取りに合わせる。と説明されています。
確かにモックを使えば、あるオブジェクトがそのモックにほしいメソッドを書きたくなるので、役割・メソッド(インターフェース)が見えてくる気がします。 あるオブジェクトに別のオブジェクトに必要なメソッドがわかったときに、そのオブジェクトに直接ロジックを書き始めるよりも、とりあえずメソッドだけ定義しておいて、モックを使ったほうがひとつのオブジェクトに集中できるし、視点が「使う側」なので、相手を「なんらかの仕事をする者」として見れますよね。

●リスクとトレードオフ:やりすぎには注意しましょう!
・1つのサンプルでモックの数が増えたら、結びつきがつよくなっているかも
・テストダブルを入れ子にし始めたら、それらを分離できる証拠
・「とりあえず」で指定したモックは偽のOKを出す危険があるので、高レベルの自動テストを導入しよう

●ここで、他のテストダブルフレームワークの使い方の説明がされます。
RSpec::configure do |config|
config.mock_with <フレームワークID>
end
フレームワークIDには:rspec,:mocha,:flexmoch,:rrのいずれかがある。デフォルトは:rspec

●その他を使うにはカスタムアダプタを作成します。とあり、MockFrameworkAdapterの内容について説明されています。
RSpec::configure do |config|
config.mock_with CustomMockAdapter
end
※そのファイルをどこにおけばいいかについては触れられていないのですが、どうやらコレのことらしく、私の環境の場合は以下のディレクトリでした。
~/.rvm/gems/ruby-1.9.2-p290/gems/rspec-core-2.8.0/lib/rspec/core/mocking

 

いやー。14章も長かった。
正直な話、14章を一度読んでも何にもわかりませんでした。例を交えてわかりやすく説明してくれているのですが、サンプルコードのキモがドコなのかわからず、本文がどこを指しているのかわからなかったからです。実装経験の多い方であればおそらくすぐにわかるのだと思いますが、Rubyでさえ初心者の私には無理でした。

くじけそうになりましたが、避けて通れないと思い直し、わからないながらもこの先の章をだーっと呼んでいくと、19章からRailsに対しての記述があります。そこを読み進めていくと、おー、わかるわかる。

途中で本を読むのをやめて、今進めているRailsのプロジェクトにわからないながらRSpecやCucumberを実際に使ってみました。

その後で14章に戻ってくると、前はわからなかった概念がよくわかるようになっていました。

ここまで読んできてこの本は、実践に則してよくまとめられている一方で、文章がなぜかわかりづらいと感じます。内容が抽象的な概念を説明してたりするので、主語述語やセンテンスの関係性があいまいな日本語にそのまま変換すると、わかりづらくなるんですかね。

とはいえRSpecを題材にした日本語の書籍といえば、現段階ではほぼこの本だけですから、ありがたく読ませていただきます。15章につづく。

 

 

 

Ruby on Rails: The RSpec Book 読破記録: 10章から13章まで

■10章からはBDDの実情をコレまでのプロジェクトの進め方について比較しながら説明がされます。
●従来:
計画 > 分析 > 設計 > コーディング > テスト(ユーザー受け入れテスト、パフォーマンステスト) > 導入
●アジャイル:
・ プロセスやツールよりも個人との対話
・包括的なドキュメントよりも動くソフトウェア
・契約交渉よりも顧客とのコラボレーション
・計画に従うことよりも変化の対応
というマニフェストどおり、短いイテレーションを繰り返しながら作り上げる手法
●BDD
従来のものよりもアジャイルの方法がよいが、その方法をとることが難しいので、それをなるべく簡単に(というか確実に)するためにあるのがBDDだと読み取りました。

■11章からは「BDDとは何か」というような内容です。
●原則
・十分といったら十分
・ステークホルダーに価値をもたらす
・すべては振る舞いから

●フィーチャー(ステークホルダーが考えるひとまとまりの機能)=[ストーリー,ストーリー,ストーリ](実装する段階でひとまとまりにできるもの)
●ストーリーとは
・タイトル
・ナラティブ ←誰が[ステークホルダー]がこうしたい[フィーチャー]ので~~[利益]
as a [ステークホルダー], I want [フィーチャー] so that [利益]
もしくは
in order to [利益], a [ステークホルダー] wants to [フィーチャー]
・受け入れ基準 ←シナリオ=[ステップ,ステップ,ステップ] ←所有者はテスター
[The one where] the account is locked
[The one where] the password is invalid
それぞれのステップが、前提・イベント・結果のいずれかにあたる。シナリオを必ずこの3つでそろえる必要はない。
シナリオはステークホルダーが行うことと同じ方法でアプリケーションとやり取りするようにする。
シナリオは必ずしも統合テストである必要はない。つまり機能の部分を切り出して行うことができるということだと理解しました。

■12章ではようやくRspecの記述方法や用語の意味を説明してくれています。
・サブジェクトコード  ←RSpecを使って振る舞いを指定するコード
・エクスペクテーション ←期待される振る舞いを表現したもので、サブジェクトコードに書く。(Test::Unitでいうアサーション)
・サンプルコード ←サブジェクトコードの使用法と、特定のコンテキストにおけるエクスペクテーションの実行可能なサンプル。。意味がわかりません。とにかくサブジェクトコードのまえに書く。これを記述する意味は設計とドキュメント化のためであり、ついでにこれはテストになる、と。(Test:Unitでいうテスト)。つまり設計やドキュメント化でもあるテストといったところですかね。
・サンプルグループ ←サンプルコードのグループ(Test::Unitでいうテストケース)
・スペック ←サンプルグループを記述したファイル。

用語の説明のあと、Rspecの構文に関する基本的なメソッド・保留のやりかた・before,after,around・ヘルパーメソッドの共有とサンプルの共有・入れ子のメリットデメリットなどを説明してくれます。

■ 13章ではエクスペクテーションの説明があります。
オブジェクト.should expectation
オブジェクト.should_not expectation
●expectation
include (something)
respond_to(something)
raise_error(sometiong)
●等価評価のいろいろ
○オブジェクト.should == something
×オブジェクト.should != sometiong
○オブジェクト.should_not == something
●そのほか、有働小数点演算のときどうするか・評価文が複数行にまたがるときどうするかなどがの説明もあります。
●shouldのほかにexpect{}.to change{}をつかって状態の変化が正しくされているか評価する方法が示されます。
changeの後にはchange{}.to()、change{}.from()、change{}.by()と続きます。
●エラーを期待する例も紹介されています。
expect{do_something_risky}.to raise_error
●期待される異例な状況を表現するためのthrow/catchもここで説明されます。
lambda{do_something_risky}.should throw_symbol
?expect toと同違うんでしょう?そもそもlambdaって いう単語を知らないので調べてみたらラムダ計算というのがあるみたいですね。expectとの違いは使いどころであり、代替可能なこともわかりました。
●空のマッチャとしてarray.should be_emptyが紹介されます。
●同様にRuby述語に対応していることが示されます。これを述語マッチャというらしいです。
user.should be_in_role(“admin”) は user.in_role?(“admin”) == trueと同じ
他にも
user.should be_a_~~やbe_an~~というように使ってもいいと。
でもparser.should be_can_parse(“sometext”)というのは動作はするのだがおかしい文になってしまうので、カスタムマッチャを作成してなんとかすることもできると。これは後で説明するとのことです。
●このような工夫が他にもあることの例としてmethod_missingとマッチャを組み合わせることが紹介されます。
request_parameters.has_key?(:id).should == true

request_parameters.should have_key(:id)
と表現できる。have_で始まるものをhas_と解釈してくれるらしいです。
●そのほかにもコレクションに対する評価方法について、コレクションを持っているパターンとコレクションが持っているというパターンでの書き方を紹介しています。
field.players.select{|p| p.team == home_team}.length == 9←フィールドにいるホームチームを取り出して、数が9であることを確認している。

home_team.should have(9).players_on(field)←ホームチームのフィールドにいるプレーヤーが9にんであることを確認している。
この例の場合はhome_teamオブジェクトにplayers_onメソッドがあることが前提となっている。ない場合はNoMethodErrorが発生する。

collection.should have(37).items
「この場合のitemsは純粋な糖衣構文です。」意味がわかりません。。Wikipediaによると糖衣構文 というのは人間にとってわかりやすくすることを目的として作られた構文ということらしいです。これ(have)について後で説明するとあるので、その時点でわかることを期待します。
●文字列もコレクションだといいます。
“this string”.should have(11).characters
●コレクションに対するエクスペクテーションの精度を表現する方法が説明されます。
haveの亜種として
・have_exactly() = have()
・have_at_least()
・have_at_most()
が紹介されます。
●haveの仕組みがここでやっと説明されます。
have()メソッドは戻り値がRSpec::Matchers::Haveクラスのインスタンスです。
コレクションに期待される数で初期化されてます。
result.should have(3).things は result.should(Have.new(3).things) と同じであると。
私なりの理解ではhaveはHaveクラスを3で初期化します。3で初期化されたHaveクラスは「コレクションの数は3に違いない」という意気込みを持ちます。resultには(変数でもメソッドでもとにかく)thingsが必要で、それがコレクションだとHaveは考えます。そしてHaveはそのコレクションのlength(なければsize)をみて3かどうかを評価すると。
要するに3とresult.things.lengthを評価しているのと同じになりますね。「人間にとってわかりやすくする」というのは時にややこしくするという側面をもちます。文章としてみたときはわかりやすいが、構文としてみたときにはブラックボックスが増えているということです。実際にlengthとsizeの意味が違った場合にややこしいことになると本文中でも指摘されています。

●演算子を使う有用なケースについて、1~10の数字を乱数として10000回出力したうち1000回ぐらいは1がでるが、ばらつきがあるはずなので出る回数を980~1020であれば問題ないとする場合
it “10000回ためしたら1は10%ぐらい出現するはず、ばらつきがあっても±2%(980~1020回)になるはず” do
result.occurrences_of(1).should be_greater_than_or_equal_to(980)
result.occurrences_of(1).should be_less_than_or_equal_to(1020)
end
↓演算子のほうがわかりやすいかも
it “10000回ためしたら1は10%ぐらい出現するはず、ばらつきがあっても±2%(980~1020回)になるはず” do
result.occurrences_of(1).should be >= 980
result.occurrences_of(1).should be <= 1020
end
使える演算子は==,=~,be <,be <=,be >,be >=です。
●ここで自動的に生成された説明についての考察がなされます。それが表現したいものになることはほとんどないので、どのように対処するかについてふんわりとした意見が書いてあります。
● サンプルの主語を明確にするためのsubject()メソッドを使用してbeforeブロックでのインスタンス変数を定義する代わりに使用すると同時に、itなどのを直接書くことができるようになります。javascriptのwith構文のような感じに見えます。
describe Person do
subject{Person.new(:birthdate => 10.years.ago)}
specify{subject.should be_eligible_to_vote}
end

describe Person do
subject {Person.new(:birthdate=> 10.years.ago)}

it {should be_eligible_to_vote}
end
●暗黙的な主語が説明されます。subject構文がない場合にdescribeの後に指定したクラスをnewしてsubjectとして使用するらしいです。当然この構文が使えるのはインスタンス化に引数がないものに限られます。
describe RSpecUser do
it{should be_happy}
end

いやー。長い章でしたが、RSpecに関する胸のつかえがとれた感じがします。
RSpecはテストと同時にドキュメントでもあるので、人間によって文章として読みやすくすることがひとつの重要な役割です。
そのため糖衣構文をふんだんにつかう必要がありました。故にブラックボックスは増え、どのように記述すればよいかについてリファレンスを追っていっても単に記憶されるだけで、仕組みとしては理解できないので、なんとなく腑に落ちない状況でした。似たようなことがRubyを勉強したときもありましたが、それよりもさらに高度に(いい意味で)ややこしくされています。
また、describeもcontentもitも動作としてはほとんど同じだという点も、この仕様を受け入れづらかった原因でした。「このようにも書けるし、このようにも書ける」といったことが多く、いたずらにメソッドを増やしているようにしか思えませんでした。そういうわけで「テストする」ということに思考の視点があるときは一体どれを書けばいいのか迷うことが多々ありました。「仕様は頭の中にある。はやくテストをしなければ」とあせっていたわけです。それよりもまず「仕様」を書きましょうと。それが「テスト」になりますよと。いろいろな書き方をマスターすることで、思考の視点を「仕様を書く」ということにシフトできれば、すらすらと書けるようになる気がしてきました。

Ruby on Rails: The RSpec Book 読破記録: 1章から9章まで

RSpecに関して勉強を始めて1週間たったのですが、なかなか日本語での情報もなく、バージョンが違ったりすることでも戸惑い、テストをするためのコーディングにえらい時間がかかり、本末転倒の状態にありました。よい情報はないことかと調べたら「The RSpec Book」という本をみつけ、さっそく購入。

この手の洋書を翻訳したような本は、サンプルを進めていくとつまづくことも多いので、記録を残すことにしました。
この記録はこの本を読んで行く中での感想や考察あるいは単にメモです。 要点をまとめたようなものではありません。つまりこの本を手に取っている方が、別の人間がどのようにこの本を読んだか知ることによって理解を深めることに役立つか、この本を買おうか悩んでいる人がざっとどんな本なのかを知ることに役立つか、そのどちらかだとおもいます。

まず、適当なフォルダを作って、
gem install rspec –version 2.0.0
gem install cucumber –version 0.9.2

を実行する。ちなみに環境はWindows7上のVirtualBox上に作ったUbuntuです。

cucumberのインストール時になにやらエラーが発生。
ERROR: Failed to build gem native extension.

ネットで調べたがよくわからず無視、とにかく先に進みます。
■14ページの
cucumber features
で予想された出力と違うものが出現。
undefined method ‘describe’ for main:object (NoMethod Error)
ネットで調べると同様のエラーがありました。

どうやら本ではファイルのおき場所が
・/hello/数字/features/greeter_says_hello.feature
・/hello/数字/greeter_spec.rb

となっているが、先述のリンク先のように
・/hello/数字/features/greeter_says_hello.feature
・/hello/数字/spec/greeter_spec.rb

としなければなりませんでした。疑問はのこるものの、先に進みます。

■3章を通過したところで疑問は増していきます。サンプルどおりにコードを作っていっても実行するところがなく、「あ、これは説明だけなのね」とわかります。

■気にせず4章に入ってすぐに「ダウンロードファイル」とあります。なに~~。このような本にダウンロードコードがあることは普通でしょうが、こんな本の途中にURLが記述されているのは珍しい。。3章までで不要なコードを書かされた(構成上しかたのないことであるとはわかっていますが)ので今後はダウンロードしたコードを使うことに決めました。
先ほどのディレクトリの間違いもここではっきりしました。ダウンロードファイルのなかでは正しく、本通りにすすめると必ずエラーになるようです。

■5章の終わりにプログラムを実行する場面があります。環境によってちょっとコードを変えなければいけないようですが、私の環境ではダウンロードしたコードそのままで実行できました。

■ひとつのコードを書いては説明があり、それが9章まで続きます。丁寧すぎていらいらするぐらいです。この時点ではじめのプロジェクトは終了し、アプリケーションを一つ作ったことになっています。 しかし、計画>コーディング>デバッグやリファクタリングというやり方になれすぎてしまっているのか、そもそもテストを書くことへの難しさを感じざるを得ず、生産性が本当にあがるという実感が持てません。単純なアプリケーションを作るだけでもコレだけのテストが必要であることを思うと、好意的に見れば、もしこれを行わない場合、アプリケーションのリリース時には未テストの状態である項目がどれだけあるか、悪寒を感じます。一方で、コレだけのテストを実装前に書き出すことができるのか、などという漠然とした不安があります。ただこのBDDという手順を踏むことで別の不安を取り除くことができ、テストの書き方さえ慣れてしまえばそこにかかる労力よりもはるかに多くのものを得られるような期待感をもって10章へ進みます。

Ruby on Rails アノテーション TODO,FIXME,OPTIMIZE

後から実装はアノテーションのコメントをいれて後で参照する。

コードに下記のように入れておくと

def validate_each(record, attributes, value)
  # TODO: 後から実装
end

下記のコマンドであとから参照できる。

$ rake notes

ウィンドウズだと日本語が文字化けするらしいので注意

Ruby on Rails 3.2.0 ビヘイビア駆動型開発RSpecのスタートアップ

アプリケーションが複雑になってきて、疎結合でがんばっていてもどうしてもひとつ直すと一方に影響してしまう状況になったので、やっぱりテストもきちんとしなければと思いなおし調べた結果、RSpecというのがどうやらよいらしい。

一応デフォルトのTest:Unitを使っていたのですが、どうせ本格的に導入するならみなさんが「いい」といっているほうがいいにきまってます。

ということでRSpecをはじめました。

が、情報が少ない!いきなりつまずいてしまいました。

ネット上の記事によっていろいろ違うことが書いてあります。どれも環境によって使い方が違うのか、短い間に仕様がいろいろ変わったのかなと想像していますが。
なのであくまで私の環境においてどのようにはじめたかを記述します。

環境は下記のとおり

  • OS:Windows7上にVirtualBoxにてUbuntuをインストール
  • ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux]
  • Rails 3.2.0

まず、インストールです。Gemfileに下記を記入

gem ‘rspec-rails’, ‘>= 2.6.0’, :group => [:development, :test]

$ bundle installを実行してインストールされたことを確認。

次にプロジェクトにRSpecを初期設定します。
$rails g rspec:install

この状態でscaffoldなどを使うと自動的にspec/のなかにmodels/hoge_spec.rbなどのファイルができます。

これらのファイルは自分で作ってもOKなのですが、だーっと作ってもらえるのはやっぱりうれしい。
ところが私の場合プロジェクトの途中ですのですでにたくさんのモデルやcontrollerを作ってしまっていました。

すでに存在するものに対してspecファイルを作るには以下のとおり。

$ rails generate rspec:model Hogehoge

コントローラも同様に作れる模様。

その後で
$ rake  spec
ですべてのテストを実行できます。

■なやんだところ

一部の記事にはRSpecをインストールした後、
$ spec -v
でインストールされたかどうかを確認する。とありましたが
$ rspec -v
で確認することができました。

Gemファイルとして提供されているものではなくRailsのプラグインとして導入するよいと薦めている記事もあります。 これによってその後の操作が違うようです。

Ruby on Rails Ver.3.2.0: generate scaffoldを取り消す手順

$ rails destroy scaffold 取り消したいScaffold名

でappのmodel,view,controllerの各要素やhelper,javascript,stylesheetなどgenerate scaffoldで作られたものがすべて削除される。

が、DBが残っている。

のでそれも消す。下記はsqlite3でgtoupsテーブルを削除する例

sqlite3 db/development.sqlite3 #接続
sqlite> .tables #テーブルがあることを確認
sqlite> DROP TABLE groups; #削除
sqlite> .tables  #テーブルが削除されたことを確認