Hatena::Grouprubyist

うんたらかんたらRuby RSSフィード

2010-04-11RE: スはスペックのス~RSpecによるテスト駆動開発の実演

RE: スはスペックのス~RSpecによるテスト駆動開発の実演

| RE: スはスペックのス~RSpecによるテスト駆動開発の実演 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - RE: スはスペックのス~RSpecによるテスト駆動開発の実演 - うんたらかんたらRuby

20110216追記

すいません、修正後ソースが見えなくなってました。



前回の続き(スはスペックのス~RSpecによるテスト駆動開発の実演 - うんたらかんたらRuby - Rubyist)。

テストコードをリファクタリングしてみた。

余談だけど、gitで随時コミットするのがいいと思った。


describeのnest

全てGameに対するテストなのでnestしてみた。

対象を説明する時は describe, 状況を説明する時は context。


beforeを一纏め

#修正前抜粋
  context "すべてガターの場合" do 
    before do
      @game = Game.new
      20.times { roll_gutter }
    end

    it "スコアは0点" do
      @game.score.should == 0
    end
  end

beforeセクションは、Game.newが重複しまくっているので

これを一纏めに。

beforeセクションの内容は、テストの準備という位置づけの方が

いいような気がする(あとでもう少し調べる)ので

各beforeセクションで実行しているテスト条件の設定をitセクションへ移動してみた。


subjectを設定

#修正前
@game.score.should == 0

全テストで比較する対象が@game.scoreなので

subjectを設定

#修正後
  subject{ @game.score }


  should == 0

contextをさらにnest

(ここは余計かも)group化した方が見やすいかもと思ったので

10フレーム目の3つのテストを

contextで更にnestしてみた。

#修正後
  context "10フレーム目の場合" do
    context "1投目がストライクの場合" do
      it "スコアは17点" do
        18.times { roll_gutter }
        roll_strike
        @game.roll(3)
        @game.roll(4) # 17
        should == 17
      end
    end

    context "2投目がストライクの場合" do
      it "スコアは14点" do
        19.times { roll_gutter }
        roll_strike
        @game.roll(4) # 14
        should == 14
      end
    end

    context "スペアの場合" do
      it "スコアは14点" do
        18.times { roll_gutter }
        roll_spare
        @game.roll(4) # 14
        @game.score.should == 14
      end
    end
  end

修正前全部

require File.expand_path(File.dirname(__FILE__) + "/../game.rb")
#[1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6] => 133

describe Game, "すべてガターの場合" do
  before do
    @game = Game.new
    20.times { roll_gutter }
  end

  it "スコアは0点" do
    @game.score.should == 0
  end
end

describe Game, "すべて1ピンの場合" do
  before do
    @game = Game.new
    20.times { @game.roll(1) }
  end

  it "スコアは20点" do
    @game.score.should == 20
  end
end

describe Game, "ストライクの場合" do
  before do
    @game = Game.new
    roll_strike
    @game.roll(3)
    @game.roll(4)
    18.times { roll_gutter }
  end

  it "スコアは24点" do
    @game.score.should == 24
  end
end

describe Game, "パーフェクトゲーム場合" do
  before do
    @game = Game.new
    12.times { roll_strike }
  end

  it "スコアは300点" do
    @game.score.should == 300
  end
end

describe Game, "スペアの場合" do
  before do
    @game = Game.new
    roll_spare
    @game.roll(4)
    @game.roll(3) # 21
    16.times { roll_gutter }
  end

  it "スコアは21点" do
    @game.score.should == 21
  end
end

#[1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6] => 133
describe Game, "Uncle Bobの受け入れゲームの場合" do
  before do
    @game = Game.new
    [1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6].each {|pin| @game.roll pin}
  end

  it "スコアは133点" do
    @game.score.should == 133
  end
end

describe Game, "10フレーム目の1投目がストライクの場合" do
  before do
    @game = Game.new
    18.times { roll_gutter }
    roll_strike
    @game.roll(3)
    @game.roll(4) # 17
  end

  it "スコアは17点" do
    @game.score.should == 17
  end
end

describe Game, "10フレーム目の2投目がストライクの場合" do
  before do
    @game = Game.new
    19.times { roll_gutter }
    roll_strike
    @game.roll(4) # 14
  end

  it "スコアは14点" do
    @game.score.should == 14
  end
end

describe Game, "10フレーム目でスペアの場合" do
  before do
    @game = Game.new
    18.times { roll_gutter }
    roll_spare
    @game.roll(4) # 14
  end

  it "スコアは14点" do
    @game.score.should == 14
  end
end

private
def roll_gutter
  @game.roll(0)
end

def roll_strike
  @game.roll(10)
end

def roll_spare
  @game.roll(5)
  @game.roll(5)
end

修正後全部

多少見やすくなったかな。

require File.expand_path(File.dirname(__FILE__) + "/../game.rb")
#require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
#[1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6] => 133

describe Game do
  before do
    @game = Game.new
  end

  subject{ @game.score }

  context "すべてガターの場合" do
    it "スコアは0点" do
      20.times { roll_gutter }
      should == 0
    end
  end

  context "すべて1ピンの場合" do
    it "スコアは20点" do
      20.times { @game.roll(1) }
      should == 20
    end
  end

  context "ストライクの場合" do
    it "スコアは24点" do
      roll_strike
      @game.roll(3)
      @game.roll(4)
      18.times { roll_gutter }
      should == 24
    end
  end

  context "パーフェクトゲーム場合" do
    it "スコアは300点" do
      12.times { roll_strike }
      should == 300
    end
  end

  context Game, "スペアの場合" do
    it "スコアは21点" do
      roll_spare
      @game.roll(4)
      @game.roll(3) # 21
      16.times { roll_gutter }
      should == 21
    end
  end

  #[1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6] => 133
  context "Uncle Bobの受け入れゲームの場合" do
    it "スコアは133点" do
      [1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6].each {|pin| @game.roll pin}
      should == 133
    end
  end

  context "10フレーム目の場合" do
    context "1投目がストライクの場合" do
      it "スコアは17点" do
        18.times { roll_gutter }
        roll_strike
        @game.roll(3)
        @game.roll(4) # 17
        should == 17
      end
    end

    context "2投目がストライクの場合" do
      it "スコアは14点" do
        19.times { roll_gutter }
        roll_strike
        @game.roll(4) # 14
        should == 14
      end
    end

    context "スペアの場合" do
      it "スコアは14点" do
        18.times { roll_gutter }
        roll_spare
        @game.roll(4) # 14
        @game.score.should == 14
      end
    end
  end
end

private
def roll_gutter
  @game.roll(0)
end

def roll_strike
  @game.roll(10)
end

def roll_spare
  @game.roll(5)
  @game.roll(5)
end


テスト結果

spec -c -fs spec/game_spec.rb

Game すべてガターの場合
- スコアは0点

Game すべて1ピンの場合
- スコアは20点

Game ストライクの場合
- スコアは24点

Game パーフェクトゲーム場合
- スコアは300点

Game Game スペアの場合
- スコアは21点

Game Uncle Bobの受け入れゲームの場合
- スコアは133点

Game 10フレーム目の場合 1投目がストライクの場合
- スコアは17点

Game 10フレーム目の場合 2投目がストライクの場合
- スコアは14点

Game 10フレーム目の場合 スペアの場合
- スコアは14点

Finished in 0.007972 seconds

9 examples, 0 failures

kakutanikakutani2010/04/14 15:25動画を丁寧に観ていただけて嬉しいです。ありがとうございます。あれは2年前のセッションなのと入門的な内容なので、いまやるならもうちょっと違う書き方がありそうですね。subjectとかnested contextはすでに使われているので、ちょっと違う感じでまとめてみました。 http://gist.github.com/365496 こっちのほうが読みやすいというつもりは無いのですが、共通処理の括り出しかたの一例として参考になれば幸いです。実行結果はこんな感じです。さいきんは --format は s よりも n がおすすめです。 http://gist.github.com/365501

rochefortrochefort2010/04/14 23:41おおお、角谷さんコメントありがとうございます!
しかもテストまで書き直してくださったなんて。
ブロック渡してテストするなんて、全く思いつきませんでした。
これはすっきりですね。
--format n もいいです。
t_wadaさんやmoroさんやるびまの記事を拝見して、最近rspec始めたところなので本当に勉強になります。

kakutanikakutani2010/04/15 01:03書き直しとか滅相もないです。ありがたいなあ、と思ったので自分でもちょっとやってみただけなので。ブロック渡し云々も、id:rochefort のリファクタした後のコードを見て「なんか他にないかなー」と無理矢理考えたところでもあるので、あんまり間に受けないでください(いつもこう書いてるわけではないです!)。 自分の日記にも感想を書いてみました: http://kakutani.com/20100414.html#p03

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100411