TDDBC横浜の感想

TDDBC横浜にスタッフとして参加してきました。

TDD自体がはじめてなわけではないですが、まだまだ経験が浅いので、いい勉強になりました。

個人的に勉強になった点を中心にまとめます。ある程度TDDをやり始めた人がハマる点、この手があったかと思う点が中心になると思います。

なお、個人的には野球に詳しくなかったり、好きじゃなかったり、苦手だったりするので、別の例で書きます。

レッド、グリーン、『リファクタリング

TDDというと、まずテストを書いてレッドになるのを確認し、実装をしてグリーンにし、その後リファクタリングをしてコードを整理します。これが、レッド、グリーン、リファクタリングです。

レッドグリーンには気が向いていたのですが、その直後にリファクタリングをするということにはこれまで意識がまわっていませんでした。
(無意識に多少はやっていたような気もしますが。)

イテレーションの中に、リファクタリングが組み込まれることで、常にコードをきれいな状態に保つことができるようです。

仮実装

テストコードもバグります。テストを書いたことがある人なら誰でも経験済みだと思います。

例えば、文字列の数を3倍して文字列で返すTripleモジュールのtripleメソッドを作ろうとしましたが、9とqをタイポしてしまいました。

describe Triple do
  describe '.triple' do
    subject {Triple.triple '3'}
    it {should == 'q'}
  end
end

実装前にテストを書いたら、Tripleという定数がないと言われてしまいますし、空のtripleメソッドを作ったところでも、戻り値が違うと言われて、順調に進んでいるように見えます。しかし、いざ実装してみても、なぜかグリーンになりません。

そこで、仮実装です。

module Triple
  def self.triple(n)
  end
end

の状態から、一気に実装せずに、

module Triple
  def self.triple(n)
    '9'
  end
end

こうします。こんなの当然テストが通るはずです。が、通りません

ここまでシンプルなメソッドだと、メソッドがバグっているというよりも、テストがバグっていることを疑うことのほうが自然です。

ここでエラーメッセージをよく見てみると、'q'とか書いてあります。これで、テストコードの間違いも見つけることができました。

describe Triple do
  describe '.triple' do
    subject {Triple.triple '3'}
    it {should == '9'}
  end
end

ここでnが4の場合のテストも書くと再びレッドになるので、そこから実装を始めます。このような方法を、三角測量というそうです。

describe Triple do
  describe '.triple' do
    context 'when n is 3' do
      subject {Triple.triple '3'}
      it {should == '9'}
    end
    context 'when n is 4' do
      subject {Triple.triple '4'}
      it {should == '12'}
    end
  end
end
module Triple
  def self.triple(n)
    (n.to_i * 3).to_s
  end
end

パラメタライズドテスト

しかし、このようなテストを書くと、冗長な気がしてしまいます。

  subject {Triple.triple '3'}
  it {should == '9'}

  subject {Triple.triple '4'}
  it {should == '12'}

は重複しているように見えます。

そこで、RSpecではletを使うんだということを教えてもらいました*1

describe Triple do
  describe '.triple' do
    subject { Triple.triple n }

    context 'when n is 3' do
      let(:n) { '3' }
      it {should == '9'}
    end
    context 'when n is 4' do
      let(:n) { '4' }
      it {should == '12'}
    end
  end
end

先ほどよりも、'3' を渡したら '9' が返ってきて、'4' を渡したら '12' が返ってくるということが明確になったと思います。beforeの前処理があると、さらにありがたみが分かる気がします。

テストをパラメタ化して、ある値に対してある値が返ってくるという組みを渡すとテストされることを、パラメタライズドテストというようです。

その観点からすると、この例だと少し弱い気がします。
もっと良い書き方が分かる人がいたら教えて下さいm(_ _)m

パラレルチェンジ

リファクタリングをすると、これまで動いていたテストが真っ赤っかになることがあります。クラスやメソッドの仕様を変えると、当然そのクラスやメソッドのテストは落ちることになります。

そこでよくやりがちなのが、えいやとテストコードにも手を加えることです。テストのリファクタリングも重要なのですが、両方を一気に変えてしまうのは危険だなと思っていました。

そこで、パラレルチェンジをするといいそうです。

まず、古いコード、テストを残したまま、それと同等の新しいコードでのテストを書きます。そして、リファクタリングします。両方合わせてテストが通ることを確認してから、古いコードを消します。

すると、安全になりました。

最後に

スタッフとして参加しましたが、スタッフというのはおいしい立場ですね。

ぜひ参加したいイベントがあるのならば、スタッフに応募するといいというのは、やっぱり本当ですね。

番外編(Rubyのカッコ)

(うろ覚えなので、セリフはイメージです。脚色があるかもしれません。)

Rubyを使っていると、Javaでカッコを忘れる」という話から…

@sakairyota「昔はRubyのカッコを省略出来るだけ省略してたけど、どうなんだろう?最近はすこし書くようになったけど。」
@1syo さん 「@sakairyota はカッコを省略しすぎ。」
@sakairyota 「Rubyでカッコをどこまで書くか最近迷ってるんですけど、どうなんですか?」
@t_wada さん 「メソッド定義のカッコを書くとか、」
@sakairyota 「メソッド定義のカッコは最近書くようになったけど、メソッド呼び出しは迷ってます。」
@t_wada さん 「メソッド呼び出しとして書くときはカッコを付けて、宣言的に書くときはカッコを付けない。カッコをつけるとgrepしやすい」
@sakairyota (!! grepという観点があったか!)
@sakairyota 「Rubyの『(』はメソッドの直後に書かないといけないから、『(』もつけてgrepするんですね!」
@t_wada さん 「そうそう」


最近、git grepとか使い始めるようになってきたけど,さて、どうしようかな…

*1:Groovyだときれいに書けるとかなんとかという話が