「テスト駆動開発」をPythonで書き直してみた 6
書籍「テスト駆動開発」をPythonで書き直してみたシリーズの第6弾です。すでに書籍のコードとは大きく乖離し始めていますが一応参考書籍は明示しておきます。過去の記事はこちらです。
pyhaya.hatenablog.com
- 作者: Kent Beck,和田卓人
- 出版社/メーカー: オーム社
- 発売日: 2017/10/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
通貨同士の足し算を実装する
最初に通貨の掛け算はtimesメソッドで実装していました。今回は、足し算を実装します。足し算の場合には掛け算とは異なり書けるほうもかけられるほうもMoneyオブジェクトであることに注意しなくてはいけません。
まずはテストから書きます。
テストを書く
tests/test_money.py
import sys sys.path.append('../src') import unittest from money import Money class MoneyTest(unittest.TestCase): def testMultiplication(self): five = Money.dollar(5) self.assertEqual(Money.dollar(10), five.times(2)) self.assertEqual(Money.dollar(15), five.times(3)) def testFrancMultiplication(self): five = Money.franc(5) self.assertEqual(Money.franc(10), five.times(2)) self.assertEqual(Money.franc(15), five.times(3)) def testEquality(self): self.assertNotEqual(Money.franc(5), Money.dollar(5)) def testSimpleAddition(self): # <- 追加 sum_ = Money.dollar(5).plus(Money.dollar(5)) self.assertEqual(sum_, Money.dollar(10)) if __name__ == '__main__': unittest.main()
テストが通るようにコードを書きなおす
テストを通します。
src/money.py
class Money: def __init__(self, amount, currency): self.amount = amount self.currency = currency def __eq__(self, other): return self.__dict__ == other.__dict__ @staticmethod def dollar(amount): return Money(amount, 'USD') @staticmethod def franc(amount): return Money(amount, 'CHF') def times(self, multiplier): return Money(self.amount * multiplier, self.currency) def plus(self, addend): amount = self.amount + addend.amount return Money(amount, self.currency)
もう少し頑張る
これで通貨の掛け算、そして通貨同士の足し算を実装できたわけですが、何か違和感がありました。Pythonでは特殊メソッドを使って基本的な演算が簡単に実装できるので、これを使ったほうが自然なコードになるはずです。
実現したいことをテストで表現します。
tests/test_money.py
import sys sys.path.append('../src') import unittest from money import Money class MoneyTest(unittest.TestCase): def testMultiplication(self): five = Money.dollar(5) self.assertEqual(Money.dollar(10), five * 2) # <- 変更 self.assertEqual(Money.dollar(15), five * 3) # <- 変更 def testFrancMultiplication(self): five = Money.franc(5) self.assertEqual(Money.franc(10), five * 2) # <- 変更 self.assertEqual(Money.franc(15), five * 3) # <- 変更 def testEquality(self): self.assertNotEqual(Money.franc(5), Money.dollar(5)) def testSimpleAddition(self): sum_ = Money.dollar(5) + Money.dollar(5) # <- 変更 self.assertEqual(sum_, Money.dollar(10)) if __name__ == '__main__': unittest.main()
やはりこのほうが自然な気がします。では、これに合わせてソースコードを変更します。
src/money.py
class Money: def __init__(self, amount, currency): self.amount = amount self.currency = currency def __eq__(self, other): return self.__dict__ == other.__dict__ def __add__(self, other): return Money(self.amount + other.amount, self.currency) def __mul__(self, multiplier): return Money(self.amount * multiplier, self.currency) @staticmethod def dollar(amount): return Money(amount, 'USD') @staticmethod def franc(amount): return Money(amount, 'CHF')
これで通ります。
テストを見直す
この段階でもう一度テストコードを見直してみます。すると、testFrancMultiplication
はもう必要ない気がしてきます。これは前回までで、FrancクラスとDollarクラスが分かれていたからこそ意味があったものでMoneyクラスに統合された状態ではtestMultiplication
でもう十分信頼性を確かめられています。
import sys sys.path.append('../src') import unittest from money import Money class MoneyTest(unittest.TestCase): def testMultiplication(self): five = Money.dollar(5) self.assertEqual(Money.dollar(10), five * 2) self.assertEqual(Money.dollar(15), five * 3) def testEquality(self): self.assertNotEqual(Money.franc(5), Money.dollar(5)) def testSimpleAddition(self): sum_ = Money.dollar(5) + Money.dollar(5) self.assertEqual(sum_, Money.dollar(10)) if __name__ == '__main__': unittest.main()
コードはGitHub上にありますので、ご自由にお使いください。
github.com
次回記事はこちら
pyhaya.hatenablog.com