stack‎ > ‎

stack-kata1

スケルトンを作る
$ 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

0 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
  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

スタック スタックが空である場合
- #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
redはredだけど何か間違って書いてる空気をかもし出している
でもそのまま進む

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とかあとで見たときものすごく意味不明になるからやめよう!ってアジャイルの本に書いてあったのを読んだ
実際に出てきた!しかもわかんなくなりそう!
Comments