2012年03月20日

[Ruby] RubyでTDD 勉強会

以前、一緒に働いていた@tokoromが勉強会を開いてくれるというので行って来ました。
参加者は僕と@e_aureum
本当なら@u_s_kも参加予定だったのですが体調不良で欠席。
講師側として@u1tnkも予定が合えば…ということだったのですが、多忙のため断念。
3人での開催となりました。

勉強会の内容は「RubyでTDD」。RubyとRSpecを使ってTDDをやってみるというもの。
僕も@e_aureumもRubyは初心者だったので、本当に簡単なところから入っていきました。
ちょっと実際の順番と違ったり、TDDとしておかしなところがあるかも知れませんが、そこは私の記憶力のなさのせいです。

■Calc
とりあえず、練習として計算を行うクラスでTDDしてみることになりました。
引数を1つ取る足し算と引き算ができます。値は累積されていきます。そんなクラスを作ります。
TDDの基本、まずはテストから書きます。

RSpecではテストコードは「○○_spec.rb」と命名するのが通例のようです。
まず、calc_spec.rbを作ります。

calc
┗Calc_spec.rb

-- calc_spec.rb --

require "Calc"

describe "Calc" do
it "0 + 3 = 3" do
calc = Calc.new
calc.add(3).should == 3
end
end

こんな感じでしょうか。(勉強会のときは確かいきなりこんなに書きませんでしたけど)
実行します。

実行は「rspec -I . calc_rspec.rb」
結果として、「F」が表示されたらNG。「.」が表示されたらOKだそうです。
結果は...どっちでもなくてエラーになりますね…。当然ですね、クラスがありません。
それではcalcクラスを作ります。

calc
┣Calc.rb
┗Calc_spec.rb

-- calc_spec.rb --

class Calc
end

クラスができたのでテストを実行します。
今度は実行はできましたが、「F」です。何がダメなのか出力を見ます。
F

Failures:

1) Calc 0 + 3 = 3
Failure/Error: calc.add(3).should == 3
NoMethodError:
undefined method `add' for #
# ./spec/Calc_spec.rb:7:in `block (2 levels) in '

Finished in 0.005 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/Calc_spec.rb:5 # Calc 0 + 3 = 3

NoMethodErrorと出ているので、メソッドがないことがわかります。
とりあえず、メソッドを用意して0 + 3 = 3を満たすコードを書きます。
class Calc
def add(val)
3
end
end

Rubyは初めてだったのですが、最後に評価した式(?)が戻り値になるって面白いですね。
ここでもう一度テスト実行します。
(そういえば、当日は空のメソッドを一度作ってからテストしたことを思い出しました)
.

Finished in 0.007 seconds
1 example, 0 failures

無事通りました。成功です。完璧です。
なお、この時点ではメインのクラスとテストのクラスが同じディレクトリにありますが、specというディレクトリを作ってそちらにテストクラスを入れておくと良いようです。

calc
┣Calc.rb
┗spec
 ┗Calc_spec.rb

こんな感じ。
calcディレクトリにいるとき「rspec -I .」で実行できるようになるとのこと。

では、確認のためにテストを追加します。

it "0 + 5 + 2 = 7" do
calc = Calc.new
calc.add(5).should == 5
calc.add(2).should == 7
end

テスト結果は…
.F

Failures:

1) Calc 0 + 5 + 2 = 7
Failure/Error: calc.add(5).should == 5
expected: 5
got: 3 (using ==)
# ./spec/calc_spec.rb:12:in `block (2 levels) in '

Finished in 0.007 seconds
2 examples, 1 failure

Failed examples:

結果が「.F」となっているので、最初のテストはパスしたけど、追加したテストで失敗しているようです。
まぁ、3しか返さないメソッドなので当然ですね。修整します。
class Calc
def initialize
@value = 0
end

def add(val)
@value += val
end
end

通りました。ばっちりです。
initializeというのがjavaでいうコンストラクタ的なものらしいです。
@〜でメンバ変数だそうです。宣言なしで使えるってのはちょっと不安がありますけども。

続いては、テストコードのリファクタリングを行なうようです。
ちょっとこのへん記憶が曖昧なので、ガッと修正後のコードを載せます。
require "Calc"

describe Calc do
before(:each) do
@target = Calc.new
end
subject { @target }

it "0 + 3 = 3" do
subject.add(3).should == 3
end

it "0 + 5 + 2 = 7" do
subject.add(5).should == 5
subject.add(2).should == 7
end
end

修正点はこんな感じ

  • describeの後のダブルクォートを取った。特定のクラスの明示的な指定?だとか?
  • before部分。テストの前にやらせたいことを書くらしい。:eachだと各テスト毎、:allだと全体で1回だって。
  • subject部分。イマイチよくわかってません。あとで調べよう。

で、またテスト実行します。

..

Finished in 0.006 seconds
2 examples, 0 failures

うん、成功しますね。
とりあえず、練習はこんな感じ。
続きは…後日書けたら書きます。
タグ:ruby TDD RSpec
posted by t2low at 12:49| Ruby