スケルトンを作る $ mkdir stack $ cd stack $ mkdir lib $ mkdir test $ vi test/stack_spec.rb ファイルとクラスの名前はstackにする。 test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do end $ spec -cfs test/stack_spec.rb /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- stack (LoadError) ファイルがないので作る $ vi lib/stack.rb class Stack end $ spec -cfs test/stack_spec.rb Finished in 0.003839 seconds 何も書いてないのでgreenになった。0 examples, 0 failures test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack = Stack.new @stack.empty?.should be_true end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと (FAILED - 1) 1) NoMethodError in 'スタック スタックが空である場合 #empty? にtrueを返すこと' undefined method `empty?' for #<Stack:0xb7e33da4> ./test/stack_spec.rb:10: Finished in 0.006008 seconds 1 example, 1 failure メソッドがない。 なお、マッチャの一覧は RSpecの標準Matcher一覧表 - Text::Easyhacking http://d.hatena.ne.jp/keisukefukuda/20080124/p1 が助かった empty?メソッドを作る lib/stack.rb class Stack def empty? end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと (FAILED - 1) 1) 'スタック スタックが空である場合 #empty? にtrueを返すこと' FAILED expected true, got nil ./test/stack_spec.rb:10: Finished in 0.011869 seconds 1 example, 1 failure nilが返ってred lib/stack.rb class Stack def empty? true end end trueを返すようにする(fakeit) $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと Finished in 0.006147 seconds 1 example, 0 failures green! pushとtopの振る舞いを一度に決めるらしい なぜか分からないけど沿ってやってみる pushしないとtopしようがないからか test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack = Stack.new @stack.empty?.should be_true end end describe "1を入れて1を出す場合" do it "#top が1であること" do @stack = Stack.new @stack.push(1) @stack.top.should == 1 end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること (FAILED - 1) 1) NoMethodError in 'スタック 1を入れて1を出す場合 #top が1であること' undefined method `push' for #<Stack:0xb7d4ef38> ./test/stack_spec.rb:17: Finished in 0.006918 seconds 2 examples, 1 failure pushがないので作る lib/stack.rb class Stack def empty? true end def push(box) end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること (FAILED - 1) 1) NoMethodError in 'スタック 1を入れて1を出す場合 #top が1であること' undefined method `top' for #<Stack:0xb7d46dec> ./test/stack_spec.rb:18: Finished in 0.010491 seconds 2 examples, 1 failure 今度はtopがないのでつくる lib/stack.rb class Stack def empty? true end def push(box) end def top end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること (FAILED - 1) 1) 'スタック 1を入れて1を出す場合 #top が1であること' FAILED expected: 1, got: nil (using ==) ./test/stack_spec.rb:18: Finished in 0.009099 seconds 2 examples, 1 failure 1を期待しているのに1が返ってない 1をfakeitで返してやる lib/stack.rb class Stack def empty? true end def push(box) end def top 1 end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること Finished in 0.00881 seconds 2 examples, 0 failures green! グリーンのままリファクタリング test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do #@stack = Stack.new @stack.empty?.should be_true end end describe "1を入れて1を出す場合" do it "#top が1であること" do #@stack = Stack.new @stack.push(1) @stack.top.should == 1 end end end グリーン 入れたものを吐き出すように修正 ここの「飛躍」してる部分がよくわかんない。「知ってる」ことしかかけないように思う。 lib/stack.rb class Stack def initialize @container end def empty? true end def push(box) @container = box end def top @container end end 次。pushしたらsizeが1増えるexampleを書く test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end end describe "1を入れて1を出す場合" do it "#top が1であること" do @stack.push(1) @stack.top.should == 1 end end describe "1を入れて1を出す場合" do it "size が1増えること" do @stack.push(1) @stack.size.should == 1 end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること スタック 1を入れて1を出す場合 - size が1増えること (FAILED - 1) 1) NoMethodError in 'スタック 1を入れて1を出す場合 size が1増えること' undefined method `size' for #<Stack:0xb7e36004 @container=1> ./test/stack_spec.rb:26: Finished in 0.010144 seconds 3 examples, 1 failure sizeがないのでsizeを作る $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること スタック 1を入れて1を出す場合 - size が1増えること (FAILED - 1) 1) 'スタック 1を入れて1を出す場合 size が1増えること' FAILED expected: 1, got: nil (using ==) ./test/stack_spec.rb:26: Finished in 0.009205 seconds 3 examples, 1 failure sizeを実装する lib/stack.rb class Stack def initialize @container end def empty? true end def push(box) @container = box end def top @container end def size 1 end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること スタック 1を入れて1を出す場合 - size が1増えること Finished in 0.009539 seconds 3 examples, 0 failures グリーン!別のexampleをかく test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end end describe "1を入れて1を出す場合" do it "#top が1であること" do @stack.push(1) @stack.top.should == 1 end end describe "1を入れて1を出す場合" do it "size が1増えること" do @stack.push(1) @stack.size.should == 1 end it "size が1増えること" do @stack.push(1) @stack.size.should == 2 end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること スタック 1を入れて1を出す場合 - size が1増えること - size が1増えること (FAILED - 1) 1) 'スタック 1を入れて1を出す場合 size が1増えること' FAILED expected: 2, got: 1 (using ==) ./test/stack_spec.rb:30: Finished in 0.010986 seconds 4 examples, 1 failure 赤 かんちがいしてた describeの中でもit shouldは毎回上の式の影響を受けずに評価される(?)のね そもそもdescribeで全体囲っていたんだった なおした test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end end describe "1を入れて1を出す場合" do before do @stack.push(1) end it "#top が1であること" do @stack.top.should == 1 end it "size が1増えること" do @stack.size.should == 1 end it "size が2増えること" do @stack.push(1) @stack.size.should == 2 end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れて1を出す場合 - #top が1であること - size が1増えること - size が2増えること Finished in 0.007999 seconds 4 examples, 0 failures グリーンになった フェイクが失敗するテストを考える test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end end describe "1を入れる場合" do before do @stack.push(1) end it "#top が1であること" do @stack.top.should == 1 end it "size が1増えること" do @stack.size.should == 1 end it "#push したらsize が2に増えること" do @stack.push(1) @stack.size.should == 2 end it "#empty? にfalseを返すこと" do @stack.empty?.should be_false end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 もくろみどおり失敗した- #empty? にtrueを返すこと スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと (FAILED - 1) 1) 'スタック 1を入れる場合 #empty? にfalseを返すこと' FAILED expected false, got true ./test/stack_spec.rb:32: Finished in 0.00953 seconds 5 examples, 1 failure lib/stack.rb class Stack def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top @container end def size @size end end sizeが0ならtrue $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと Finished in 0.009562 seconds 5 examples, 0 failures グリーン!これなら変な飛躍がない 納得 何もpushされていないときのpopのふるまい test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do @stack.pop.should raise_error(EmptyStackException) end end describe "1を入れる場合" do before do @stack.push(1) end it "#top が1であること" do @stack.top.should == 1 end it "size が1増えること" do @stack.size.should == 1 end it "#push したらsize が2に増えること" do @stack.push(1) @stack.size.should == 2 end it "#empty? にfalseを返すこと" do @stack.empty?.should be_false end end end たぶんこんなかんじ $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 メソッドが無いむね怒られる メソッド作る- #empty? にtrueを返すこと - #pop に例外エラーを返すこと (FAILED - 1) スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと 1) NoMethodError in 'スタック スタックが空である場合 #pop に例外エラーを返すこと' undefined method `pop' for #<Stack:0xb7d737fc @size=0> ./test/stack_spec.rb:15: Finished in 0.009584 seconds 6 examples, 1 failure lib/stack.rb class Stack def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top @container end def size @size end def pop end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 redはredだけど何か間違って書いてる空気をかもし出している- #empty? にtrueを返すこと - #pop に例外エラーを返すこと (FAILED - 1) スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと 1) NameError in 'スタック スタックが空である場合 #pop に例外エラーを返すこと' uninitialized constant EmptyStackException ./test/stack_spec.rb:15: Finished in 0.010046 seconds 6 examples, 1 failure でもそのまま進む lib/stack.rb class Stack def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top @container end def size @size end def pop raise "EmptyStackException" end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと - #pop に例外エラーを返すこと (FAILED - 1) スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと 1) RuntimeError in 'スタック スタックが空である場合 #pop に例外エラーを返すこと' EmptyStackException /home/sane/studyruby/stack/lib/stack.rb:25:in `pop' ./test/stack_spec.rb:15: Finished in 0.0116 seconds 6 examples, 1 failure しっぱい。やっぱり間違っていたぽい 例外の起こし方が間違ってた。exampleの書き方も違ってた。ズルして答えっぽいものを見る RSpec をもっと理解したかったので、まとめを作りました - takihiroの日記 http://d.hatena.ne.jp/takihiro/20081108/1226114504 [テスト] should change に見る UnitTest と RSpec の違い | ヽ( ・∀・)ノくまくまー(2009-03-30) http://wota.jp/ac/?date=20090330#p01 あああなるほどー! テストはこうなる test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end end describe "1を入れる場合" do before do @stack.push(1) end it "#top が1であること" do @stack.top.should == 1 end it "size が1増えること" do @stack.size.should == 1 end it "#push したらsize が2に増えること" do @stack.push(1) @stack.size.should == 2 end it "#empty? にfalseを返すこと" do @stack.empty?.should be_false end end end lib/stack.rb class Stack class EmptyStackException < RuntimeError; end def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top @container end def size @size end def pop raise EmptyStackException end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 グリーン!- #empty? にtrueを返すこと - #pop に例外エラーを返すこと スタック 1を入れる場合 - #top が1であること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと Finished in 0.009952 seconds 6 examples, 0 failures テストを抽象化してみた test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end end end グリーン! とは言っても、「プログラマのためのテスト」ならこっちの方が安心だけど、「納品する(あるいは納品できる状態にある)テスト」としたら、具体的に「1」使っているほうがうれしいかもしれない。 ケースバイケースか。 今度は、pushしてpopしてたら例外を返さない振る舞い test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.pop }.should_not raise_error(Stack::EmptyStackException) end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと - #pop に例外エラーを返すこと スタック 1つ加える場合 - #top が加えたものであること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと - #pop に例外エラーを返さないこと (FAILED - 1) 1) 'スタック 1つ加える場合 #pop に例外エラーを返さないこと' FAILED expected no Stack::EmptyStackException, got #<Stack::EmptyStackException: Stack::EmptyStackException> ./test/stack_spec.rb:38: Finished in 0.011251 seconds 7 examples, 1 failure 例外を返しているのでレッド lib/stack.rb class Stack class EmptyStackException < RuntimeError; end def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top @container end def size @size end def pop if empty? raise EmptyStackException end end end ぐりーんにならない!exampleが間違ってた 修正。 test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end end end グリーン! 空でtopしたら例外返す振る舞いを決める test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end it "#top に例外エラーを返すこと" do lambda { @stack.top }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと - #pop に例外エラーを返すこと - #top に例外エラーを返すこと (FAILED - 1) スタック 1つ加える場合 - #top が加えたものであること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと - #pop に例外エラーを返さないこと 1) 'スタック スタックが空である場合 #top に例外エラーを返すこと' FAILED expected Stack::EmptyStackException but nothing was raised ./test/stack_spec.rb:18: Finished in 0.012141 seconds 8 examples, 1 failure 空の場合例外を実装 lib/stack.rb class Stack class EmptyStackException < RuntimeError; end def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top unless empty? @container else raise EmptyStackException end end def size @size end def pop if empty? raise EmptyStackException end end end グリーン! リファクタリングする lib/stack.rb class Stack グリーン!class EmptyStackException < RuntimeError; end def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top check_empty @container end def size @size end def pop check_empty end def check_empty if empty? raise EmptyStackException end end end pushしてpopしたらsizeが+1-1になること test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end it "#top に例外エラーを返すこと" do lambda { @stack.top }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end it "pop したらサイズが+1 -1なこと" do lambda { @stack.push(Object.new) @stack.pop }.should change(@stack, :size).by(+1-1) end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 レッド!- #empty? にtrueを返すこと - #pop に例外エラーを返すこと - #top に例外エラーを返すこと スタック 1つ加える場合 - #top が加えたものであること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと - #pop に例外エラーを返さないこと - pop したらサイズが+1 -1なこと (FAILED - 1) 1) 'スタック 1つ加える場合 pop したらサイズが+1 -1なこと' FAILED size should have been changed by 0, but was changed by 1 ./test/stack_spec.rb:47: Finished in 0.013012 seconds 9 examples, 1 failure lib/stack.rb class Stack class EmptyStackException < RuntimeError; end def initialize @container @size = 0 end def initialize @container @size = 0 end def empty? @size == 0 end def push(box) @container = box @size += 1 end def top check_empty @container end def size @size end def pop check_empty @size -= 1 end def check_empty if empty? raise EmptyStackException end end end グリーン! 最後に先入れ後出しのコンテナを実装する test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end it "#top に例外エラーを返すこと" do lambda { @stack.top }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end it "pop したらサイズが+1 -1なこと" do lambda { @stack.push(Object.new) @stack.pop }.should change(@stack, :size).by(+1-1) end end describe "後入れ先出しの場合" do it "二つ入れて一つ出したら先に加えたものが返ること" do @item = Object.new @stack.push(@item) @stack.push(Object.new) @stack.pop @stack.top.should == @item end end end $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 レッド!- #empty? にtrueを返すこと - #pop に例外エラーを返すこと - #top に例外エラーを返すこと スタック 1つ加える場合 - #top が加えたものであること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと - #pop に例外エラーを返さないこと - pop したらサイズが+1 -1なこと スタック 後入れ先出しの場合 - 二つ入れて一つ出したら先に加えたものが返ること (FAILED - 1) 1) 'スタック 後入れ先出しの場合 二つ入れて一つ出したら先に加えたものが返ること' FAILED expected: #<Object:0xb7d64798>, got: #<Object:0xb7d64784> (using ==) ./test/stack_spec.rb:60: Finished in 0.013562 seconds 10 examples, 1 failure まきもどして、数字保持部分を配列にする 後入れ先出しのテストをいったんコメントアウト グリーン! lib/stack.rb class Stack class EmptyStackException < RuntimeError; end def initialize @container = [] @size = 0 end def empty? @size == 0 end def push(box) @container << box @size += 1 end def top check_empty @container[@size - 1] end def size @size end def pop check_empty @size -= 1 end def check_empty if empty? raise EmptyStackException end end end グリーン! もいちどテストを足す test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) グリーン!になっちゃダメなんだけど…require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end it "#top に例外エラーを返すこと" do lambda { @stack.top }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end it "pop したらサイズが+1 -1なこと" do lambda { @stack.push(Object.new) @stack.pop }.should change(@stack, :size).by(+1-1) end end describe "後入れ先出しの場合" do it "二つ入れて一つ出したら先に加えたものが返ること" do @item = Object.new @stack.push(@item) @stack.push(Object.new) @stack.pop @stack.top.should == @item end end end どこが間違っているか分からないのでちょっと保留 配列に格納して、番号をsizeにした時点で条件満たしてしまっていることかな 不明 test/stack_speck.rb $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) require 'rubygems' require 'spec' require 'stack' describe "スタック" do before do @stack = Stack.new end describe "スタックが空である場合" do it "#empty? にtrueを返すこと" do @stack.empty?.should be_true end it "#pop に例外エラーを返すこと" do lambda { @stack.pop }.should raise_error(Stack::EmptyStackException) end it "#top に例外エラーを返すこと" do lambda { @stack.top }.should raise_error(Stack::EmptyStackException) end end describe "1つ加える場合" do it "#top が加えたものであること" do @item = Object.new @stack.push(@item) @stack.top.should == @item end it "size が1増えること" do lambda { @stack.push(Object.new) }.should change(@stack, :size).by(1) end it "#push したらsize が2に増えること" do lambda { 2.times { @stack.push(Object.new) } }.should change(@stack, :size).by(2) end it "#empty? にfalseを返すこと" do @stack.push(Object.new) @stack.empty?.should be_false end it "#pop に例外エラーを返さないこと" do lambda { @stack.push(Object.new) @stack.pop }.should_not raise_error(Stack::EmptyStackException) end it "pop したらサイズが+1 -1なこと" do lambda { @stack.push(Object.new) @stack.pop }.should change(@stack, :size).by(+1-1) end end describe "後入れ先出しの場合" do it "二つ入れて一つ出したら先に加えたものが返ること" do @item = Object.new @stack.push(@item) @stack.push(Object.new) @stack.pop @stack.top.should == @item end end describe "いろいろ受け入れテスト" do it "いれて「いれて」いれてだしていれてだして「一番上」" do @item = Object.new @stack.push(Object.new) @stack.push(@item) @stack.push(Object.new) @stack.pop @stack.push(Object.new) @stack.pop @stack.top.should == @item end it "3入れ2出し3入れ2出してsize" do 3.times{ @stack.push(Object.new) } 2.times{ @stack.pop } 3.times{ @stack.push(Object.new) } 2.times{ @stack.pop } @stack.size.should == 2 end end end 納得いかなかったので受け入れテストを増やしてみた $ spec -cfs test/stack_spec.rb スタック スタックが空である場合 - #empty? にtrueを返すこと - #pop に例外エラーを返すこと - #top に例外エラーを返すこと スタック 1つ加える場合 - #top が加えたものであること - size が1増えること - #push したらsize が2に増えること - #empty? にfalseを返すこと - #pop に例外エラーを返さないこと - pop したらサイズが+1 -1なこと スタック 後入れ先出しの場合 - 二つ入れて一つ出したら先に加えたものが返ること スタック いろいろ受け入れテスト - いれて「いれて」いれてだしていれてだして「一番上」 - 3入れ2出し3入れ2出してsize Finished in 0.01875 seconds 12 examples, 0 failures グリーン! ただ納得いかない…「stack」から「pop」したらその要素は消したい でもsizeはきちんと正しい数字を返してくるんだ なぜならそうなるように書いたから! ぬぬぬぬ 課題 +1とか-1とかあとで見たときものすごく意味不明になるからやめよう!ってアジャイルの本に書いてあったのを読んだ 実際に出てきた!しかもわかんなくなりそう! |
stack >