Subscribed unsubscribe Subscribe Subscribe

目の前に僕らの道がある

勉強会とか、技術的にはまったことのメモ

テスト駆動開発入門をPythonで写経してみた。16

とりあえずコードだけ。

原書は相変わらず会社に置きっぱなしなので記憶を頼りに解説を書こうかと思っていましたが、かなり昔に書いたコードなので思い出せませんでした。タイプスタンプを見る限りでは3ヶ月くらい前でした。

やりっぱなしじゃ勉強した意味がないので、復習の意味でも近いうちに必ず書こうと思います。

Part2に関しては原書自体がそもそもPythonでかかれているのでRubyで写経しようかな考えています。ただ"テスト駆動開発入門をRubyで写経してみた。"シリーズが追いついて来たらの話なのでいつになるのやらです。

今回は基本と言うことでいわゆるPyunitでユニットテストを書いてみたんですが、新しいユニットテストフレームワークがあるみたいで、そちらで書き直してみても面白いかもしれません。noseに関してはもうすでにやっている人がいるみたいなのでpikzieを使ってみようかな。

money.py

#!/usr/bin/env python
# coding: utf-8
"""テスト駆動開発入門 16章 最後に抽象化
"""

class Expression(object):
    def reduce(self, bank, currency_to):
        raise NotImplementedError

    def __add__(self, addend):
        raise NotImplementedError

    def __mul__(self, multiplier):
        raise NotImplementedError

class Money(Expression):
    def __init__(self, amount, currency):
        self._amount = amount
        self._currency = currency

    def __mul__(self, multiplier):
        """原書のtimesメソッド
        """
        return Money(self._amount * multiplier, self._currency)

    @staticmethod
    def dollar(amount):
        return Money(amount, "USD")

    @staticmethod
    def franc(amount):
        return Money(amount, "CHF")

    def currency(self):
        return self._currency

    def reduce(self, bank, currency_to):
        rate = bank.rate(self._currency, currency_to)
        return Money(self._amount/rate, currency_to)

    def __eq__(self, other):
        """原書のequalsメソッド
        """
        return self._amount == other._amount and \
                self.currency() == other.currency()

    def __str__(self):
        """デバッグ用出力文字列
        """
        return str(self._amount) + " " + str(self._currency)

    __unicode__ = __str__
    __repr__ = __str__

    def __add__(self, addend):
        """原書のplusメソッド
        """
        return Sum(self, addend)

class Bank(object):
    def __init__(self):
        self.rates = dict()

    def reduce(self, source, currency_to):
        return source.reduce(self, currency_to)

    def rate(self, currency_from, currency_to):
        if currency_from == currency_to:
            return 1
        return self.rates[(currency_from, currency_to)]

    def add_rate(self, currency_from, currency_to, rate):
        self.rates[(currency_from, currency_to)] = rate
        # Hashtableをdictionaryで実装

class Sum(Expression):
    def __init__(self, augend, addend):
        self.augend = augend
        self.addend = addend

    def reduce(self, bank, currency_to):
        amount = self.augend.reduce(bank, currency_to)._amount + \
                self.addend.reduce(bank, currency_to)._amount
        return Money(amount, currency_to)

    def __add__(self, addend):
        return Sum(self, addend)

    def __mul__(self, multiplier):
        return Sum(self.augend * multiplier, self.addend * multiplier)

money_test.py

#!/usr/bin/env python
# coding: utf-8
"""テスト駆動開発入門 16章 最後に抽象化
"""

import unittest
from money import *

class TestMoney(unittest.TestCase):
    """moneyモジュールのテスト
    """

    def testMultiplication(self):
        """かけ算のテスト
        """
        five = Money.dollar(5)
        self.assertEqual(Money.dollar(10), five * 2)
        self.assertEqual(Money.dollar(15), five * 3)

    def testFrancMultiplication(self):
        """Francクラスのかけ算のテスト
        """

        five = Money.franc(5)
        self.assertEqual(Money.franc(10), five * 2)
        self.assertEqual(Money.franc(15), five * 3)

    def testEuality(self):
        """同値テスト
        """

        self.assertTrue(    Money.dollar(5) == Money.dollar(5) )
        self.assertTrue(not Money.dollar(5) == Money.dollar(6) )
        self.assertTrue(not Money.franc(5)  == Money.dollar(5) )

    def testCurrency(self):
        """通貨単位のテスト
        """

        self.assertEqual("USD", Money.dollar(1).currency() )
        self.assertEqual("CHF", Money.franc(1).currency() )

    def testSimpleAddition(self):
        """足し算のテスト
        """

        five = Money.dollar(5)
        sum = five + five
        self.assertTrue(Money.dollar(10), sum)
        bank = Bank()
        reduced = bank.reduce(sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)

    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        result = five + five
        sum = result
        self.assertEqual(five, sum.addend)
        self.assertEqual(five, sum.augend)

    def testReduceSum(self):
        sum = Sum(Money.dollar(3), Money.dollar(4) )
        bank = Bank()
        result = bank.reduce(sum, "USD")
        self.assertEqual(Money.dollar(7), result)

    def testReduceMoney(self):
        bank = Bank()
        result = bank.reduce(Money.dollar(1), "USD")
        self.assertEqual(Money.dollar(1), result)

    def testReduceMoneyDifferentCurrency(self):
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        result = bank.reduce(Money.franc(2), "USD")
        self.assertEqual(Money.dollar(1), result)

    def testIdentityRate(self):
        self.assertEqual(1, Bank().rate("USD", "USD") )

    def testMixedAddition(self):
        five_bucks = Money.dollar(5)
        ten_francs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        result = bank.reduce(five_bucks + ten_francs, "USD")
        self.assertEqual(Money.dollar(10), result)

    def testSumPlusMoney(self):
        five_bucks = Money.dollar(5)
        ten_francs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        sum = Sum(five_bucks, ten_francs) + five_bucks
        result = bank.reduce(sum, "USD")
        self.assertEqual(Money.dollar(15), result)

    def testSumTimes(self):
        five_bucks = Money.dollar(5)
        ten_francs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        sum = Sum(five_bucks, ten_francs) * 2
        result = bank.reduce(sum, "USD")
        self.assertEqual(Money.dollar(20), result)

#    def testPlusSameCurrencyReturnsMoney(self):
#        sum = Money.dollar(1) + Money.dollar(1)
#        self.assertTrue(isinstance(sum, Money) )

if __name__ == "__main__":
    unittest.main()

テスト駆動開発入門

テスト駆動開発入門

  • 作者: ケントベック,Kent Beck,長瀬嘉秀,テクノロジックアート
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2003/09
  • メディア: 単行本
  • 購入: 43人 クリック: 1,002回
  • この商品を含むブログ (153件) を見る