というわけでやっとTDD開発に入ります。
ここまでにこれだけがっつり環境の確認をしているわけですから、想定外のエラーが出たら基本、新しく作ったものが間違っているはずですね。
心して行きます。
TDD開発でFizzBuzz
根本的なところでFizzBuzzの確認をしておくと、
- 最初は「1」
- 1づつ増える
- 3で割り切れるときは数字の代わりに「Fizz」
- 5で割り切れるときは数字の代わりに「Buzz」
- 3でも5でも割り切れる(つまり15の倍数)ときは数字の代わりに「FizzBuzz」
となります。
今回はTDDを使いやすいように、与えた数値にが「数値」か「Fizz」か「Buzz」か「FizzBuzz」を返すようなものを作ります。
それを1から順にループ回してあげれば普通のFizzBuzzですね。
プロジェクトの作成とセットアップ
その2でやったようにプロジェクトのセットアップをします。
ただ、今回はテストコードとソースコードは1から作ります。
前にやってるのでさらっと流します。
- プロジェクト用の階層「fizzbuzz」を作る
- 階層「fizzbuzz」からコマンドプロンプトで
bundle init
を実行 - 作成された「Gemfile」を書き換える ※前回と同じなので丸コピでもいいかも
- 階層「fizzbuzz」からコマンドプロンプトで
bundle install --path .bundle
を実行 - 次のコマンドでエラーが予想されるので、「.bundle\ruby\2.0.0\bin」中に「ruby.exe」と「rubyw.exe」をコピー
- 階層「fizzbuzz」からコマンドプロンプトで
bundle exec rspec --init
を実行
ここまでできたら準備OKです。
テストコードの作成 ちょー長い1歩目
TDDですので、最初に作るのはテストコードです。
ソースコードは後回し。
ということで、階層「fizzbuzz」の下の「spec」の下にテストコード「FizzBuzz_spec.rb」ファイルを作ります。
まず最初の条件は「最初が1」ですから、そのテストコード書きます。
「1」を与えたら「1」を返すです。
require 'bundler'
Bundler.require
require_relative '../FizzBuzz'
describe FizzBuzz do
it "should be 1" do
expect(FizzBuzz.new.fizzbuzz(1)).to eq "1"
end
end
書き方はいろいろあるようですが、今後はexpect()~で書いた方がいいよ的なことをどこかで見たので、この記法で。
実行するとエラーになります。
赤く囲った部分を見ると、「そんなファイル読み込めなかったよ!」的な感じですかね。
そりゃそうです、ソースコードファイル作ってないですから。
では「FizzBuzz.rb」という名前で空っぽのファイルを作ってあげて、また実行。
まだ当然エラーになるはずですね。
ただ、エラーが微妙に変わりました。
今度はファイルについてのエラーは出なくなり、「FizzBuzzなんてしらないよ!」になりました。
そりゃまあクラス作ってませんもんね。
じゃあ、ということでクラス作ってあげます。
class FizzBuzz
end
ほんとにクラスだけ作りました。
これももちろんエラーですね。
お、でもちょっとテストっぽくなってきたぞ?
とりあえずメソッドがないと言われているようなので、じゃあメソッドを追加。
class FizzBuzz
def fizzbuzz(num)
end
end
引数もらうけど、何もしない、にしてみました。
おお、すごいテストっぽい!
1を想定してたのに空っぽだったよ!だそうです。
わかったじゃあ1を返してやろうじゃないか。
class FizzBuzz
def fizzbuzz(num)
"1"
end
end
…安直とか言うな!
少なくとも通ったじゃないか!
というわざとらしくカメの歩みのようなことをやっておりますが、地道に進めるって大事なことだと思うんです。
どこかで同じエラー見たら、「あのときのあれだ!」ってわかりますし。
テストコードの作成 2歩目
ここからはもうちょっとさくさく行きます。
「1」を与えたら「1」を返すですが、「2」を与えたら「2」を返すでもあります。
ということでこれを書きます。
ちょっと長くなるので、付け足す分だけ書きます。
+ describe FizzBuzz do
+ it "should be 2" do
+ expect(FizzBuzz.new.fizzbuzz(2)).to eq "2"
+ end
+ end
そうすると先ほどのウルトラ安直ソースコードでは当然NGになります。
なので、与えられた数字をそのまま返すようにします。
数字のままなので、文字に変換して返してあげます。
class FizzBuzz
def fizzbuzz(num)
num.to_s
end
end
今度はさすがに一発!
テストコードの作成 3歩目
1、2と来ましたから次は3です。
ただ、3の倍数が「Fizz」ですから、ついでに6もテストしちゃいます。
3、6のどちらも「Fizz」を返すことです。
+ [3,6].each do |num|
+ describe FizzBuzz do
+ it "should be 'Fizz'" do
+ expect(FizzBuzz.new.fizzbuzz(num)).to eq "Fizz"
+ end
+ end
+ end
そしてNGになっていることを確認します。
で、3の倍数の「Fizz」を実装します。
class FizzBuzz
def fizzbuzz(num)
if num % 3 == 0 then "Fizz"
else num.to_s
end
end
end
テストもばっちり。
テストコードの作成 すっとばして最後
そんな感じで次に5の倍数、15の倍数、とやって、最終的に通ったものがこれです。
require 'bundler'
Bundler.require
require_relative '../FizzBuzz'
describe FizzBuzz do
it "should be 1" do
expect(FizzBuzz.new.fizzbuzz(1)).to eq "1"
end
end
describe FizzBuzz do
it "should be 2" do
expect(FizzBuzz.new.fizzbuzz(2)).to eq "2"
end
end
[3,6].each do |num|
describe FizzBuzz do
it "should be 'Fizz'" do
expect(FizzBuzz.new.fizzbuzz(num)).to eq "Fizz"
end
end
end
[5,10].each do |num|
describe FizzBuzz do
it "should be 'Buzz'" do
expect(FizzBuzz.new.fizzbuzz(num)).to eq "Buzz"
end
end
end
[15,30].each do |num|
describe FizzBuzz do
it "should be 'FizzBuzz'" do
expect(FizzBuzz.new.fizzbuzz(num)).to eq "FizzBuzz"
end
end
end
class FizzBuzz
def fizzbuzz(num)
if num % 15 == 0 then "FizzBuzz"
elsif num % 5 == 0 then "Buzz"
elsif num % 3 == 0 then "Fizz"
else num.to_s
end
end
end
ゆっくり進めたのでかなり長かったですねー。
でもこれで完成です。
これを使って次の小ネタを考えてますが。それはまた次の機会に。
本当はいろいろ調べた出典を書いておきたかったのですが、もうどこから何の情報取ってきたのかわからない状態になっていますorz
情報元の皆様に心から感謝してます。
私も情報出す側にならねばー。