これからはじめるTDD
C# で写経してみた。
Testプロジェクトは、頭に Test をつけたけど、後ろにつけるべきだったのかな。
注意点は、プロダクトの方にテストプロジェクトがアクセスするためには、プロダクト側の AssemblyInfo.cs に以下を記述すること。
[assembly: InternalsVisibleTo("TestKorekaraTdd")]
BowlingGame.cs
using System.Collections.Generic; using System.Linq; namespace KorekaraTdd { class BowlingGame { private Frame spareFrame; private List<Frame> strikes = new List<Frame> { }; private IList<Frame> frames = new List<Frame> { new Frame() }; public int FrameScore(int frameNo) { return frames[frameNo - 1].Score; } public void RecordShot(int pins) { frames.Last().RecordShot(pins); CalcSpareBonus(pins); CalcStrikeBonus(pins); if (frames.Last().IsFinished()) { frames.Add(new Frame()); } } public int Score() { int total = 0; foreach(var frame in frames) { total += frame.Score; } return total; } private void AddStrikeBonus(int pins) { foreach(var strike in strikes) { if (strike.IsNeedBonus()) { strike.AddBonus(pins); } } } private void CalcSpareBonus(int pins) { if (spareFrame != null && spareFrame.IsNeedBonus()) { spareFrame.AddBonus(pins); } if (frames.Last().IsSpare()) { spareFrame = frames.Last(); } } private void CalcStrikeBonus(int pins) { AddStrikeBonus(pins); if (frames.Last().IsStrike()) { RecognizeStrikeBonus(); } } private void RecognizeStrikeBonus() { strikes.Add(frames.Last()); } } }
Frame.cs
using System; namespace KorekaraTdd { class Frame { private int score = 0; private int bonusCount = 0; public int Score { get { return score + Bonus; } private set { score = value; } } public int Bonus { get; private set; } private int shotCount = 0; public Frame() { Score = 0; Bonus = 0; } public void AddBonus(int bonus) { Bonus += bonus; bonusCount++; } public bool IsFinished() { return Score >= 10 || shotCount > 1; } public bool IsSpare() { return Score >= 10 && shotCount > 1; } public bool IsStrike() { return Score >= 10 && shotCount == 1; } public bool IsNeedBonus() { if (IsSpare()) { return bonusCount < 1; } if (IsStrike()) { return bonusCount < 2; } return false; } public void RecordShot(int pins) { CheckPinsCount(pins); Score += pins; shotCount++; } private void CheckPinsCount(int pins) { if ((pins < 0 || 10 < pins) && Score + pins <= 10) { throw new ArgumentException(); } } } }
BowlingGameTest.cs
using System; using KorekaraTdd; namespace TestKorekaraTdd { using NUnit.Framework; [TestFixture] public class BowlingGameTest { BowlingGame game; [SetUp] public void SetUp() { game = new BowlingGame(); } [Test] public void 全ての投球がガター() { RecordManyShot(20, 0); Assert.That(game.Score, Is.EqualTo(0)); } [Test] public void 全ての投球で1ピンだけ倒した() { RecordManyShot(20, 1); Assert.That(game.Score, Is.EqualTo(20)); } [Test] public void スペアをとると次の投球のピン数を加算() { RecordSpare(); game.RecordShot(4); RecordManyShot(17, 0); Assert.That(game.Score, Is.EqualTo(18)); Assert.That(game.FrameScore(1), Is.EqualTo(14)); } [Test] public void 直前の投球との合計が10品でもフレーム違いはスペアではない() { foreach (var i in new int[] { 2, 5, 5, 1 }) { game.RecordShot(i); } RecordManyShot(16, 0); Assert.That(game.Score, Is.EqualTo(13)); } [Test] public void ストライクをとると次の2投分のピン数を加算() { RecordStrike(); foreach (var i in new int[] { 3, 3, 1 }) { game.RecordShot(i); } RecordManyShot(15, 0); Assert.That(game.Score, Is.EqualTo(23)); Assert.That(game.FrameScore(1), Is.EqualTo(16)); } [Test] public void 連続ストライクすなわちダブル() { RecordStrike(); RecordStrike(); foreach (var i in new int[] { 3, 1 }) { game.RecordShot(i); } RecordManyShot(14, 0); Assert.That(game.Score, Is.EqualTo(41)); Assert.That(game.FrameScore(1), Is.EqualTo(23)); Assert.That(game.FrameScore(2), Is.EqualTo(14)); } [Test] public void 連続3回ストライクすなわちターキー() { for (var i = 0; i < 3; i++) { RecordStrike(); } foreach (var i in new int[] { 3, 1 }) { game.RecordShot(i); } RecordManyShot(12, 0); Assert.That(game.Score, Is.EqualTo(71)); } [Test] public void ストライクの後のスペア() { RecordStrike(); RecordSpare(); game.RecordShot(3); RecordManyShot(15, 0); Assert.That(game.Score, Is.EqualTo(36)); } [Test] public void ダブル後のスペア() { RecordStrike(); RecordStrike(); RecordSpare(); game.RecordShot(3); RecordManyShot(13, 0); Assert.That(game.Score, Is.EqualTo(61)); } [Test] public void 全ての投球がガターの場合の第1フレームの得点() { RecordManyShot(20, 0); Assert.That(game.FrameScore(1), Is.EqualTo(0)); } [Test] public void 全ての投球が1ピンだと全フレーム2点() { RecordManyShot(20, 1); Assert.That(game.FrameScore(1), Is.EqualTo(2)); } private void RecordManyShot(int count, int pins) { for (var i = 0; i < count; i++) { game.RecordShot(pins); } } private void RecordStrike() { game.RecordShot(10); } private void RecordSpare() { RecordManyShot(2, 5); } } }
FrameTest.cs
using System; using KorekaraTdd; namespace TestKorekaraTdd { using NUnit.Framework; [TestFixture] class FrameTest { Frame frame; [SetUp] public void SetUp() { frame = new Frame(); } [Test] public void 全ての投球がガター() { frame.RecordShot(0); frame.RecordShot(0); Assert.That(frame.Score, Is.EqualTo(0)); } [Test] public void 全ての投球で1ピンだけ倒した() { frame.RecordShot(1); frame.RecordShot(1); Assert.That(frame.Score, Is.EqualTo(2)); } [Test] public void 二投するとフレームは完了する() { frame.RecordShot(1); Assert.That(frame.IsFinished(), Is.False); frame.RecordShot(1); Assert.That(frame.IsFinished(), Is.True); } [Test] public void 十ピン倒した時点でフレームは完了する() { frame.RecordShot(10); Assert.That(frame.IsFinished, Is.True); } [Test] public void 二投目で10ピン倒すとスペア() { frame.RecordShot(5); Assert.That(frame.IsSpare(), Is.False); frame.RecordShot(5); Assert.That(frame.IsSpare(), Is.True); } [Test] public void 一投目で10ピン倒すとストライク() { Assert.That(frame.IsStrike(), Is.False); frame.RecordShot(10); Assert.That(frame.IsStrike(), Is.True); } [Test] public void ボーナス点を加算する() { frame.RecordShot(5); frame.RecordShot(5); frame.AddBonus(5); Assert.That(frame.Score, Is.EqualTo(15)); } [Test] public void オープンフレームにはボーナス不要() { frame.RecordShot(3); frame.RecordShot(3); Assert.That(frame.IsNeedBonus(), Is.False); } [Test] public void スペアのボーナスは1投分で完了() { frame.RecordShot(5); frame.RecordShot(5); Assert.That(frame.IsNeedBonus(), Is.True); frame.AddBonus(5); Assert.That(frame.IsNeedBonus(), Is.False); } [Test] public void ストライクのボーナスは2投分で完了() { frame.RecordShot(10); frame.AddBonus(5); Assert.That(frame.IsNeedBonus(), Is.True); frame.AddBonus(5); Assert.That(frame.IsNeedBonus(), Is.False); } [Test] public void ピン数は0本以上() { Assert.Throws<ArgumentException>( delegate { frame.RecordShot(-1); }); } } }