ホーム DoRuby 絶対に笑ってはいけないRSpecの現場24時

絶対に笑ってはいけないRSpecの現場24時

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは、SHIMADAです。 今日は現場の泥臭いRSpec話を書きます。

新しいフィーチャーを追加するとき、specがなかなか通らない、実装が思い通りに進まない、ということがありますよね。
そんなとき、自分の書いたコードが実際にはどう動いているのか確かめる方法が欲しいです。

一番簡単で広く使われているのはデバッグプリントという手法です。
コードの中に p 変数名 と書くと、specの実行途中にその変数の中身がどうなっているか確認できます。

ここで問題になるのが、specがたくさん書いてあるとデバッグプリントが一杯出てきて肝心の調べたいケースが埋もれてしまうという点です。

ちなみに今携わっているプロジェクトのspecを見てみると、examplesの数が1,000を超えてました。

これが一般的な水準で多いのか少ないのか分かりませんが、ここまできてしまうとデバッグプリントがどかどかっと表示されてあっという間に押し流されてしまうので大変です。

そういう状況でもなんとかしようと編み出した泥臭いテクニックを以下に紹介します。

説明のために簡単なバグ入りのコードと、それを検出するテストを用意しました。

コード


class Calculator
  attr_reader :answer

  def plus(a, b)
    @answer = a + a
  end
end

テスト


describe Calculator do
  subject do
    Calculator.new
  end

  it "1 + 1 = 2" do
    subject.plus(1, 1)
    subject.answer.should == 2
  end

  it "2 + 2 = 4" do
    subject.plus(2, 2)
    subject.answer.should == 4
  end

  it "2 + 3 = 5" do
    subject.plus(2, 3)
    subject.answer.should == 5
  end
end

こんな感じでコードとテストを書いたとします。
バグがあるので “2 + 3 = 5” は成功しません。

Calculator#plusにデバッグプリントを埋め込んで、引数と計算結果を調べてみましょう。


  def plus(a, b)
p :chk1
puts "b = #{b.inspect}"
puts "b = #{b.inspect}"
    @answer = a + a
  end

まず、デバッグ用のコードは通常のコードではないことを強調するために、インデントを無視して行頭から書くようにしています。
こうすることでデバッグが終わってコミットする前に、無駄なコードを消し忘れることを防ぎます。

それから、先頭の p :chk1 は僕が個人的に使っているデバッグ用のマーカーです。
RSpecはピリオドがたくさん出力されます。
デバッグ出力が行の途中から始まると折り返しが読みにくいので、:chk1という目印を使って強制的に改行します。


..........:chk1
a = 1
b = 1
......

当然、いろんなところにデバッグプリントを入れていくときは :chk1, :chk2, :chk3… と数字を増やしていきます。

ところで、このままだと全部のテストケースでデバッグプリントが出力されてしまいます。
できれば、失敗するspecの時だけデバッグプリントを出力したいですね。
そこで、specの側に一時的にこういうものを埋め込みます。


  it "2 + 3 = 5" do
$dbg=true
    subject.plus(2, 3)
$dbg=false
    subject.answer.should == 5
  end

はい。ご覧のとおり $dbg は禁断のグローバル変数です。
グローバルなのでspec内で定義して本番コードの中から参照できます。無敵です。

ちなみにコミットする前に除去する一時的なコードなので、この変数は本来のコードと被らなければどんな名前でも構いません。

使い方は、Cでよくある


#define DEBUG


#ifdef DEBUG

のペアと同じです。


  def plus(a, b)
p :chk1 if $dbg
puts "b = #{b.inspect}" if $dbg
puts "b = #{b.inspect}" if $dbg
    @answer = a + a
  end

これで、調べたいテストケースの時だけデバッグプリントを出力させることができるようになりました。
特定のケースで、どの行でどんな挙動が起こっているか確認できると、デバッグはかなり捗ります。

あとそれから、デバッグプリントだけでは足りなくてどうしてもうまくいかない難しいケースのとき、


debugger if $dbg

としたこともあります。specの実行中この箇所に到達すると、Rubyのデバッガが起動するのでステップ、トレースしながらコードを追いかけてなんとか問題を解決することができました。

最後に、コードのバグがとれてspecが通るようになったらこれらは全部消してきれいにしてからコミットします。

タネを明かせばどうっていうこともない簡単なことですが、なかなか役に立つので今までずっと使っています。みなさんもバグに手こずった時、試してみることをお薦めします。

記事を共有

最近人気な記事