『テスト駆動開発』第1部をC#で写経してみる(第3章)

第3章 三角測量

第3章ではValue Objectを作成し、別名参照問題を防ぐ方法を見ていく。

まずはDollarオブジェクトの等価性について確認する。

namespace TddStudyTest
{
    [TestClass]
    public class MoneyTest
    {
        [TestMethod]
        public void TestMultiplication()
        {
            Dollar five = new Dollar(5);
            Dollar product = five.times(2);
            Assert.AreEqual(10, product.amount);
            product = five.times(3);
            Assert.AreEqual(15, product.amount);
        }

        [TestMethod]
        public void TestEquality()
        {
            Assert.IsTrue(new Dollar(5).Equals(new Dollar(5)));
        }
    }
}

TestEquality()を追加した。当然のごとくテストは失敗するので、trueを返すだけの仮実装を行う。

namespace TddStudy.Money
{
    public class Dollar
    {
        public int amount;

        public Dollar(int amount)
        {
            this.amount = amount;
        }

        public Dollar times(int multiplier)
        {
            return new Dollar(this.amount * multiplier);
        }

        public override bool Equals(object obj)
        {
            return true;
        }
    }
}

テストが通ったので、今度は別の等価性確認を行ってみる。

namespace TddStudyTest
{
    [TestClass]
    public class MoneyTest
    {
        [TestMethod]
        public void TestMultiplication()
        {
            Dollar five = new Dollar(5);
            Dollar product = five.times(2);
            Assert.AreEqual(10, product.amount);
            product = five.times(3);
            Assert.AreEqual(15, product.amount);
        }

        [TestMethod]
        public void TestEquality()
        {
            Assert.IsTrue(new Dollar(5).Equals(new Dollar(5)));
            Assert.IsFalse(new Dollar(5).Equals(new Dollar(6)));
        }
    }
}

追加したテストが失敗するので、ここで透過性比較を一般化する必要性が見えてくる。

namespace TddStudy.Money
{
    public class Dollar
    {
        public int amount;

        public Dollar(int amount)
        {
            this.amount = amount;
        }

        public Dollar times(int multiplier)
        {
            return new Dollar(this.amount * multiplier);
        }

        public override bool Equals(object obj)
        {
            var dollar = (Dollar)obj;
            return amount == dollar.amount;
        }
    }
}

equals()をoverrideしたときはHashCode()もoverrideする必要があるが、今の時点ではスルーする。

このように、2つの異なる視点からのテストメソッドを作成することにより、コードの一般化を行うことができた。『テスト駆動開発』では、この手法を三角測量と呼んでいる。

雑感

三角測量を用いてコードの一般化を行うケースは少ないかもしれないが、本書に書いてある通り、どうやってリファクタリングしてよいか分からないときや、設計に迷ったときは重宝しそうだ。

カテゴリーTDD

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です