Erleben Sie Teil I "Multinationale Währung" des Buches "Test Driven Development" mit Python

Die Beispiele in ** Teil I "Multilaterale Währung" ** des Buches "Test Driven Development" basieren also auf JAVA Um mein Verständnis zu vertiefen, habe ich die gleiche Praxis in Python versucht.

Der Code der testgetriebenen Entwicklungspraxis ist übrigens im folgenden Github gespeichert. https://github.com/ttsubo/study_of_test_driven_development/tree/master/python

■ So fahren Sie mit der testgetriebenen Entwicklung fort

Das im Buch "Testgetriebene Entwicklung" empfohlene Verfahren für die testgetriebene Entwicklung lautet wie folgt.

Auch in dieser Praxis werden wir dieses Verfahren so weit wie möglich befolgen.

■ Erleben Sie eine testgetriebene Entwicklung durch multinationale Währungen

Die folgenden zwei Anforderungen sollten realisiert werden.

Kapitel 1: Geld mit mehreren Währungen

Multiplizieren Sie zunächst den Betrag (Betrag pro Währungseinheit) mit einem numerischen Wert (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Wir streben einen Mechanismus namens `an. Insbesondere werden wir mit der Codierung fortfahren, so dass "$ 5 * 2 = $ 10" gilt.

(1) Schreiben Sie zuerst einen kleinen Test.

Erstellen Sie einen Test, der bestätigt, dass "$ 5 * 2 = $ 10" gilt.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        five.times(2)
        self.assertEqual(10, five.amount)

(2) Bereiten Sie den zu testenden Code vor

Stellen Sie den Code bereit, der nur die Klasse "Dollar" lädt.

example/dollar.py


class Dollar:
    pass

(3) Führen Sie den Test durch und bestätigen Sie, dass einer fehlschlägt.

Der Test schlägt fehl, weil die Dollar-Klasse nicht objektiviert werden kann.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testMultiplication FAILED                [100%]
================================== FAILURES ===================================
___________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/tests/test_money.py", line 6, in testMultiplication
    five = Dollar(5)
TypeError: Dollar() takes no arguments

(4) Nehmen Sie kleine Änderungen vor.

Definieren Sie einen Konstruktor, damit Sie die Klasse "Dollar" objektivieren können.

example/dollar.py


class Dollar:
    def __init__(self, amount):  
        self.amount = amount

(5) Führen Sie den Test erneut durch und bestätigen Sie, dass einer fehlschlägt.

Der Test schlägt fehl, weil für die "Dollar" -Klasse keine "Times" -Methode definiert ist.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testMultiplication FAILED                [100%]
================================== FAILURES ===================================
___________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/tests/test_money.py", line 7, in testMultiplication
    five.times(2)
AttributeError: 'Dollar' object has no attribute 'times'

(6) Nehmen Sie erneut kleine Änderungen vor.

`Multiplizieren Sie den Betrag mit einer Zahl, um den Betrag zu erhalten. Die "times" -Methode definiert es so, dass es sein kann.

example/dollar.py


class Dollar:
    def __init__(self, amount):  
        self.amount = amount
                                                                                    
    def times(self, multiplier):                                                    
        self.amount *= multiplier

(7) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Schließlich ist der Test erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 2: Entartete Objekte

In Kapitel 1 erhalten Sie den Betrag, indem Sie den Betrag (Betrag pro Währungseinheit) mit einer Zahl (Anzahl der Währungseinheiten) multiplizieren. Als Mechanismus namens habe ich versucht, das notwendige Minimum zu implementieren. Außerdem wird hier der Betrag (Betrag pro Währungseinheit) mit einem numerischen Wert (Anzahl der Währungseinheiten) multipliziert, um den Betrag zu erhalten. Wir werden den Mechanismus namens so erweitern, dass er viele Male aufgerufen werden kann.

(1) Schreiben Sie zuerst einen kleinen Test.

Nachdem Sie bestätigt haben, dass "$ 5 * 2 = $ 10" gilt, erstellen Sie einen Test, der auch "$ 5 * 3 = $ 15" enthält. Es wird angenommen, dass die "Dollar" -Klasse einmal zu einem Objekt gemacht wird und dann der Multiplikationswert zum Testen auf verschiedene Arten geändert wird.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        product = five.times(2)
        self.assertEqual(10, product.amount)
        product = five.times(3)
        self.assertEqual(15, product.amount)

(2) Führen Sie den Test durch und bestätigen Sie, dass einer fehlschlägt.

Der Test schlägt fehl, da wir die Logik zum Abrufen des Ausführungsergebnisses der "times" -Methode des "Dollar" -Objekts noch nicht implementiert haben.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testMultiplication FAILED                [100%]
================================== FAILURES ===================================
___________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/python/tests/test_money.py", line 8, in testMultiplication
    self.assertEqual(10, product.amount)
AttributeError: 'NoneType' object has no attribute 'amount'

(3) Nehmen Sie kleine Änderungen vor.

Implementieren Sie die Logik, um das Ausführungsergebnis der "times" -Methode des "Dollar" -Objekts zu erhalten.

--Erstellen Sie ein neues "Dollar" -Objekt, wenn Sie die "times" -Methode des "Dollar" -Objekts aufrufen

  • Speichern Sie das Multiplikationsergebnis in der neuen "Dollar" -Objektinstanzvariablen "Betrag"

example/dollar.py


class Dollar:
    def __init__(self, amount):  
        self.amount = amount
                                                                                    
    def times(self, multiplier):                                                    
        return Dollar(self.amount * multiplier)

(4) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist jetzt erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 3: Gleichheit für alle

In Kapitel 2 erhalten Sie den Betrag, indem Sie den Betrag (Betrag pro Währungseinheit) mit der Anzahl (Anzahl der Währungseinheiten) multiplizieren. Der Mechanismus namens wurde erweitert, so dass er viele Male aufgerufen werden kann. Hier werden wir einen Mechanismus hinzufügen, um zu bestätigen, dass ein $ 5 der gleiche Wert ist wie der andere $ 5. Übrigens wird die Identität des Objekts mit der speziellen Methode __eq__ von Python bestätigt.

(1) Schreiben Sie zuerst einen kleinen Test.

Erstellen Sie einen Test, um sicherzustellen, dass `ein $ 5 einem anderen $ 5 entspricht.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        product = five.times(2)
        self.assertEqual(10, product.amount)
        product = five.times(3)
        self.assertEqual(15, product.amount)
    
    def testEquality(self):
        self.assertTrue(Dollar(5) == Dollar(5))
        self.assertFalse(Dollar(5) == Dollar(6))

(2) Führen Sie den Test durch und bestätigen Sie, dass einer fehlschlägt.

Der Test schlägt jedoch fehl, da es in der Klasse "Dollar" keinen Mechanismus gibt, mit dem überprüft werden kann, ob "ein $ 5 dem anderen $ 5 entspricht".

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testEquality FAILED              [ 50%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]
================================== FAILURES ===================================
___________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/python/tests/test_money.py", line 13, in testEquality
    self.assertTrue(Dollar(5) == Dollar(5))
  File "/Users/ttsubo/.pyenv/versions/3.8.0/lib/python3.8/unittest/case.py", line 765, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true

(3) Nehmen Sie kleine Änderungen vor.

Nutzen Sie die spezielle Methode __eq__, um` ein $ 5 zu definieren, damit Sie sehen können, dass es dem anderen $ 5 entspricht.

example/dollar.py


class Dollar:
    def __init__(self, amount):  
        self.amount = amount
    
    def __eq__(self, other):
        return self.amount == other.amount
                                                                                    
    def times(self, multiplier):                                                    
        return Dollar(self.amount * multiplier)

(4) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist jetzt erfolgreich.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED              [ 50%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 4: Vorsätzlicher Test (Datenschutz)

In Kapitel 3 haben wir einen Mechanismus hinzugefügt, um sicherzustellen, dass `ein $ 5 den gleichen Wert hat wie der andere $ 5. Hier werden wir die überlappenden Teile der bisherigen Testbeschreibung mit einer klaren Sicht umgestalten. Ändern Sie außerdem die Instanzvariable "Betrag" des "Dollar" -Objekts in ein privates Mitglied.

(1) Test: Refactor MoneyTest: testMultiplication.

Die "times" -Methode der "Dollar" -Klasse gibt ein "Dollar" -Objekt zurück, dessen Betrag multipliziert mit dem "Multiplikator" -Argument in der Instanzvariablen "Betrag" des Dollar-Objekts ist. In der Testbeschreibung, die ich bisher geschrieben habe, ist es schwierig zu sagen, dass das "Dollar" -Objekt zurückgegeben wird, daher werde ich es umgestalten, um die Lesbarkeit zu verbessern.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        self.assertEqual(Dollar(10), five.times(2))
        self.assertEqual(Dollar(15), five.times(3))
    
    def testEquality(self):
        self.assertTrue(Dollar(5) == Dollar(5))
        self.assertFalse(Dollar(5) == Dollar(6))

(2) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist.

Die Tests sind nach dem Refactoring noch erfolgreich.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED              [ 50%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

(3) Nehmen Sie kleine Änderungen vor.

Ändern Sie die Instanzvariable "Betrag" des "Dollar" -Objekts in ein privates Mitglied.

example/dollar.py


class Dollar:
    def __init__(self, amount):  
        self.__amount = amount
    
    def __eq__(self, other):
        return self.__amount == other.__amount
                                                                                    
    def times(self, multiplier):                                                    
        return Dollar(self.__amount * multiplier)

(4) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED              [ 50%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 5: Wenn Sie es wagen, das Prinzip zu brechen (französisch gesprochen)

Bis zu Kapitel 4 multiplizieren Sie als eine der Mehrwährungen den Betrag (Betrag pro Währungseinheit) in Bezug auf die Dollarwährung mit einem numerischen Wert (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Wir haben den Mechanismus von `erkannt. Hier, als eine andere Währung, wird ** Frankenwährung ** den gleichen Mechanismus realisieren.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie einen Test hinzu, damit Sie denselben Test für die Dollarwährung in der Frankenwährung sehen können.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar
from example.franc import Franc

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        self.assertEqual(Dollar(10), five.times(2))
        self.assertEqual(Dollar(15), five.times(3))
    
    def testEquality(self):
        self.assertTrue(Dollar(5) == Dollar(5))
        self.assertFalse(Dollar(5) == Dollar(6))

    def testFrancMultiplication(self):
        five = Franc(5)
        self.assertEqual(Franc(10), five.times(2))
        self.assertEqual(Franc(15), five.times(3))

(2) Bereiten Sie den zu testenden Code vor

Stellen Sie den Code bereit, der nur die Klasse "Franc" lädt.

example/franc.py


class Franc:
    pass

(3) Führen Sie den Test durch und bestätigen Sie, dass einer fehlschlägt.

Der Test schlägt fehl, weil die Franc-Klasse nicht objektiviert werden kann.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication FAILED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]
================================== FAILURES ===================================
___________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/tests/test_money.py", line 16, in testFrancMultiplication
    five = Franc(5)
TypeError: Franc() takes no arguments

(4) Nehmen Sie kleine Änderungen vor.

Wir werden die Franc-Klasse definieren, indem wir uns auf das beziehen, was wir in Kapitel 1 getan haben.

example/franc.py


class Franc:
    def __init__(self, amount):  
        self.__amount = amount
    
    def __eq__(self, other):
        return self.__amount == other.__amount
                                                                                    
    def times(self, multiplier):                                                    
        return Franc(self.__amount * multiplier)

(4) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 6: Wenn Sie einen Mangel an Tests bemerken (Gleichheit für alle, Redux)

In Kapitel 5 erhalten Sie den Betrag, indem Sie den Betrag (Betrag pro Währungseinheit) in Bezug auf die Frankenwährung mit einer Zahl (Anzahl der Währungseinheiten) multiplizieren. Wir haben den Mechanismus von `erkannt, aber die Methode entsprach dem Kopieren des gesamten Mechanismus der Dollarwährung, so dass viele überlappende Teile auftraten. Um Doppelarbeit zu vermeiden, haben zunächst die "einen 5 Dollar, an denen wir in Kapitel 3 gearbeitet haben, den gleichen Wert wie die anderen 5 Dollar" und die "5 Franken" den gleichen Wert wie die anderen 5 Franken. Starten Sie den `` Teil.

(1) Schreiben Sie zuerst einen kleinen Test.

Dollar-Währungstest Fügen Sie einen Test hinzu, sodass ein $ 5 dem anderen $ 5 entspricht in Frankenwährung.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar
from example.franc import Franc

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        self.assertEqual(Dollar(10), five.times(2))
        self.assertEqual(Dollar(15), five.times(3))
    
    def testEquality(self):
        self.assertTrue(Dollar(5) == Dollar(5))
        self.assertFalse(Dollar(5) == Dollar(6))
        self.assertTrue(Franc(5) == Franc(5))
        self.assertFalse(Franc(5) == Franc(6))

    def testFrancMultiplication(self):
        five = Franc(5)
        self.assertEqual(Franc(10), five.times(2))
        self.assertEqual(Franc(15), five.times(3))

(2) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der hinzugefügte Testteil ist auch ohne Probleme erfolgreich.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

(3) Refaktorieren und entfernen Sie Duplikate.

Hier führen wir das folgende Refactoring durch.

  • Definieren Sie eine neue übergeordnete Klasse "Geld"
  • Beseitigen Sie den duplizierten Teil der speziellen Methode "eq", die in der Klasse "Dollar", "Franc" definiert ist, und machen Sie ihn in der Klasse "Geld" üblich.

example/money.py


class Money:
    def __init__(self, amount):  
        self.__amount = amount

    def __eq__(self, other):
        return self.__amount == other.__amount

example/dollar.py


from example.money import Money

class Dollar(Money):
    def __init__(self, amount):  
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Dollar(self.__amount * multiplier)

example/franc.py


from example.money import Money

class Franc(Money):
    def __init__(self, amount):  
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Franc(self.__amount * multiplier)

(4) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> NG </ font>

Unerwartet wurde der Test gelöscht. </ font>

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testEquality FAILED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication FAILED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication FAILED                [100%]
================================== FAILURES ===================================
______________________________ MoneyTest.testEquality _________________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):

...(snip)

  File "/Users/ttsubo/source/study_of_test_driven_development/example/money.py", line 6, in __eq__
    return self.__amount == other.__amount
AttributeError: 'Dollar' object has no attribute '_Money__amount'

________________________ MoneyTest.testFrancMultiplication ____________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):

...(snip)

  File "/Users/ttsubo/source/study_of_test_driven_development/example/money.py", line 6, in __eq__
    return self.__amount == other.__amount
AttributeError: 'Franc' object has no attribute '_Money__amount'

__________________________ MoneyTest.testMultiplication ______________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):

...(snip)

File "/Users/ttsubo/source/study_of_test_driven_development/example/money.py", line 6, in __eq__
    return self.__amount == other.__amount
AttributeError: 'Dollar' object has no attribute '_Money__amount'

(5) Refactor zur Behebung der Ursache des Testfehlers.

Der Grund, warum der Test fehlschlug, war der Zeitpunkt der Objektivierung von "Dollar" und "Franken", und es war notwendig, den Wert in der Instanzvariablen "Betrag" der übergeordneten Klasse "Geld" zu speichern. Korrigieren Sie daher den Code.

example/dollar.py


from example.money import Money

class Dollar(Money):
    def __init__(self, amount):  
        super(Dollar, self).__init__(amount)
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Dollar(self.__amount * multiplier)

example/franc.py


from example.money import Money

class Franc(Money):
    def __init__(self, amount):  
        super(Franc, self).__init__(amount)
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Franc(self.__amount * multiplier)

(6) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Diesmal war der Test erfolgreich. Aufgrund der testgetriebenen Entwicklung können Sie selbst einen kleinen Refactoring-Fehler sofort feststellen. </ font>

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 7: Zweifel in Tests übersetzen (Äpfel und Orangen)

Bisher haben wir "Dollarwährung" und "Frankenwährung" als mehrere Währungen unterstützt. Die Frage ist nun: "Was ist, wenn Sie ** Dollarwährung ** mit ** Frankenwährung ** vergleichen?"

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie einen Test hinzu, um sicherzustellen, dass die ** Dollarwährung ** und die ** Frankenwährung ** nicht gleich sind.

tests/test_money.py


from testtools import TestCase
from example.dollar import Dollar
from example.franc import Franc

class MoneyTest(TestCase):
    def testMultiplication(self):
        five = Dollar(5)
        self.assertEqual(Dollar(10), five.times(2))
        self.assertEqual(Dollar(15), five.times(3))
    
    def testEquality(self):
        self.assertTrue(Dollar(5) == Dollar(5))
        self.assertFalse(Dollar(5) == Dollar(6))
        self.assertTrue(Franc(5) == Franc(5))
        self.assertFalse(Franc(5) == Franc(6))
        self.assertFalse(Franc(5) == Dollar(5))

    def testFrancMultiplication(self):
        five = Franc(5)
        self.assertEqual(Franc(10), five.times(2))
        self.assertEqual(Franc(15), five.times(3))

(2) Führen Sie den Test durch. -> NG </ font>

Der Test schlägt fehl. Mit anderen Worten, das Ergebnis ist, dass der Dollar und der Franken gleich sind.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality FAILED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]
================================== FAILURES ===================================
______________________________ MoneyTest.testEquality _________________________
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: Traceback (most recent call last):
  File "/Users/ttsubo/source/study_of_test_driven_development/tests/test_money.py", line 16, in testEquality
    self.assertFalse(Franc(5) == Dollar(5))
  File "/Users/ttsubo/.pyenv/versions/3.8.0/lib/python3.8/unittest/case.py", line 759, in assertFalse
    raise self.failureException(msg)
AssertionError: True is not false

(3) Beheben Sie die Ursache des Testfehlers.

Da es notwendig war, das "Dollar" -Objekt und das "Franc" -Objekt zu vergleichen, um den Äquivalenzvergleich durchzuführen, modifizieren Sie den Teil der Beurteilungslogik der speziellen Methode "eq" der "Money" -Klasse.

example/money.py


class Money:
    def __init__(self, amount):  
        self.__amount = amount

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.__class__.__name__ == other.__class__.__name__)

(4) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Diesmal war der Test erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 8: Implementierung ausblenden (Objekte erstellen)

In den Kapiteln 6 und 7 haben wir den Äquivalenzvergleich überarbeitet. Von nun an multiplizieren Sie für eine Weile den Betrag (Betrag pro Währungseinheit) mit einer Zahl (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Versuche, Duplikate im Mechanismus zu entfernen.

(1) Schreiben Sie zuerst einen kleinen Test.

Hierbei wird angenommen, dass das folgende Refactoring als erster Schritt durchgeführt wird, um den doppelten Teil der in den Klassen "Dollar" und "Franc" definierten "Zeiten" -Methode zu eliminieren und in der "Geld" -Klasse gemeinsam zu machen. Und behebe den Test.

  • Definieren Sie die Klassenmethode "Dollar" in der Klasse "Geld", um ein "Dollar" -Objekt zu erstellen
  • Definieren Sie die Klassenmethode "Franc" in der Klasse "Money", um ein "Franc" -Objekt zu erstellen

tests/test_money.py


from testtools import TestCase
from example.money import Money

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertTrue(Money.franc(5) == Money.franc(5))
        self.assertFalse(Money.franc(5) == Money.franc(6))
        self.assertFalse(Money.franc(5) == Money.dollar(5))

    def testFrancMultiplication(self):
        five = Money.franc(5)
        self.assertEqual(Money.franc(10), five.times(2))
        self.assertEqual(Money.franc(15), five.times(3))

(2) Refactor.

Hier wird das folgende Refactoring als erster Schritt durchgeführt, um den doppelten Teil der in den Klassen "Dollar" und "Franc" definierten "Zeiten" -Methode zu eliminieren und in der "Geld" -Klasse gemeinsam zu machen.

  • Definieren Sie die Klassenmethode "Dollar" in der Klasse "Geld", um ein "Dollar" -Objekt zu erstellen
  • Definieren Sie die Klassenmethode "Franc" in der Klasse "Money", um ein "Franc" -Objekt zu erstellen

example/money.py


from abc import ABCMeta, abstractmethod
from example.dollar import Dollar
from example.franc import Franc

class Money(metaclass=ABCMeta):
    def __init__(self, amount):  
        self.__amount = amount

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.__class__.__name__ == other.__class__.__name__)

    @abstractmethod
    def times(self, multiplier):
        pass

    @classmethod
    def dollar(cls, amount):
        return Dollar(amount)

    @classmethod
    def franc(cls, amount):
        return Franc(amount)

(3) Führen Sie den Test durch. -> NG </ font>

Der Test konnte nicht gestartet werden und es ist ein Fehler aufgetreten. Es scheint, dass die Ursache der zirkuläre Import von Python-Modulen ist.

$ pytest -v

...(snip)

tests/test_money.py::MoneyTest::testEquality FAILED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication FAILED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]
=================================== ERRORs ====================================
ImportError while importing test module '/Users/ttsubo/source/study_of_test_driven_development/tests/test_money.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_money.py:2: in <module>
    from example.money import Money
example/money.py:2: in <module>
    from example.dollar import Dollar
example/dollar.py:1: in <module>
    from example.money import Money
E   ImportError: cannot import name 'Money' from partially initialized module 'example.money' (most likely due to a circular import) (/Users/ttsubo/source/study_of_test_driven_development/example/money.py)

(4) Beheben Sie die Ursache des Testfehlers.

Ändern Sie die in money.py zu definierenden Klassen "Dollar" und "Franc", da dies auf den zirkulären Import von Python-Modulen zurückzuführen ist.

example/money.py


from abc import ABCMeta, abstractmethod

class Money(metaclass=ABCMeta):
    def __init__(self, amount):  
        self.__amount = amount

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.__class__.__name__ == other.__class__.__name__)

    @abstractmethod
    def times(self, multiplier):
        pass

    @classmethod
    def dollar(cls, amount):
        return Dollar(amount)

    @classmethod
    def franc(cls, amount):
        return Franc(amount)


class Dollar(Money):
    def __init__(self, amount):  
        super(Dollar, self).__init__(amount)
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Dollar(self.__amount * multiplier)


class Franc(Money):
    def __init__(self, amount):
        super(Franc, self).__init__(amount)
        self.__amount = amount

    def times(self, multiplier):
        return Franc(self.__amount * multiplier)

(5) Führen Sie den Test erneut durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Diesmal war der Test erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 9: Anpassen der Schrittlänge (Zeiten, in denen wir leben)

Fahren Sie mit dem vorherigen Kapitel fort und multiplizieren Sie den Betrag (Betrag pro Währungseinheit) mit einem numerischen Wert (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Versuche, Duplikate im Mechanismus zu entfernen.

(1) Schreiben Sie zuerst einen kleinen Test.

Hier werden die Objekte "Dollar" und "Franc" als erster Schritt verwendet, um die Verdoppelung der in den Klassen "Dollar" und "Franc" definierten "Zeiten" -Methode zu beseitigen und sie in der Klasse "Geld" gemeinsam zu machen. Fügen Sie den Test testCurrency hinzu, vorausgesetzt, Sie wenden das Konzept der " Währung " zur Unterscheidung an.

  • Setzen Sie die Instanzvariable "__currency" beim Erstellen des "Dollar" -Objekts auf "USD".
  • Definieren Sie eine "Währungs" -Methode, damit Sie auf das private Mitglied "__currency" des "Dollar" -Objekts verweisen können
  • Setzen Sie "CHF" in der Instanzvariablen "__currency", wenn Sie das "Franc" -Objekt erstellen.
  • Definieren Sie die Methode "Währung", damit Sie auf das private Mitglied "__Währung" des Objekts "Franc" verweisen können

tests/test_money.py


from testtools import TestCase
from example.money import Money

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertTrue(Money.franc(5) == Money.franc(5))
        self.assertFalse(Money.franc(5) == Money.franc(6))
        self.assertFalse(Money.franc(5) == Money.dollar(5))

    def testFrancMultiplication(self):
        five = Money.franc(5)
        self.assertEqual(Money.franc(10), five.times(2))
        self.assertEqual(Money.franc(15), five.times(3))
    
    def testCurrency(self):
        self.assertEqual("USD", Money.dollar(1).currency())
        self.assertEqual("CHF", Money.franc(1).currency())

(2) Refactor.

Hier werden die Objekte "Dollar" und "Franc" als erster Schritt verwendet, um die Verdoppelung der in den Klassen "Dollar" und "Franc" definierten "Zeiten" -Methode zu beseitigen und sie in der Klasse "Geld" gemeinsam zu machen. Wir wenden das Konzept der "Währung" an, das eine Unterscheidung treffen soll.

  • Setzen Sie die Instanzvariable "__currency" beim Erstellen des "Dollar" -Objekts auf "USD".
  • Definieren Sie eine "Währungs" -Methode, damit Sie auf das private Mitglied "__currency" des "Dollar" -Objekts verweisen können
  • Setzen Sie "CHF" in der Instanzvariablen "__currency", wenn Sie das "Franc" -Objekt erstellen.
  • Definieren Sie die Methode "Währung", damit Sie auf das private Mitglied "__Währung" des Objekts "Franc" verweisen können

example/money.py


from abc import ABCMeta, abstractmethod

class Money(metaclass=ABCMeta):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.__class__.__name__ == other.__class__.__name__)

    @abstractmethod
    def times(self, multiplier):
        pass

    def currency(self):
        return self.__currency

    @classmethod
    def dollar(cls, amount):
        return Dollar(amount, "USD")

    @classmethod
    def franc(cls, amount):
        return Franc(amount, "CHF")


class Dollar(Money):
    def __init__(self, amount, currency):
        super().__init__(amount, currency)
        self.__amount = amount
    
    def times(self, multiplier):                                                    
        return Money.dollar(self.__amount * multiplier)


class Franc(Money):
    def __init__(self, amount, currency):
        super().__init__(amount, currency)
        self.__amount = amount

    def times(self, multiplier):
        return Money.franc(self.__amount * multiplier)

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test war erwartungsgemäß erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 25%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 50%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 75%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 10: Fragen Sie den Test (interessante Zeiten)

Multiplizieren Sie den Betrag (Betrag pro Währungseinheit), den Sie bisher begonnen haben, mit einem numerischen Wert (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Schließen Sie den Versuch ab, den duplizierten Teil des Mechanismus namens `zu entfernen.

(1) Schreiben Sie zuerst einen kleinen Test.

Multiplizieren Sie den Betrag (Betrag pro Währungseinheit) mit der Anzahl (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Fügen Sie den Test testDifferentClassEquality hinzu, vorausgesetzt, das Refactoring zum Entfernen von Duplikaten im Mechanismus ist abgeschlossen.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Franc

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertTrue(Money.franc(5) == Money.franc(5))
        self.assertFalse(Money.franc(5) == Money.franc(6))
        self.assertFalse(Money.franc(5) == Money.dollar(5))

    def testFrancMultiplication(self):
        five = Money.franc(5)
        self.assertEqual(Money.franc(10), five.times(2))
        self.assertEqual(Money.franc(15), five.times(3))
    
    def testCurrency(self):
        self.assertEqual("USD", Money.dollar(1).currency())
        self.assertEqual("CHF", Money.franc(1).currency())

    def testDifferentClassEquality(self):
        self.assertTrue(Money(10, "CHF") == Franc(10, "CHF"))

(2) Refactor.

Führen Sie das folgende Refactoring durch:

  • Löschen Sie die in der Klasse "Dollar", "Franc" definierte "Times" -Methode
  • Definieren Sie als übliche "Zeiten" -Methode in der "Geld" -Klasse --Überprüfen Sie die Methode des Äquivalenzvergleichs von "Geld" -Objekten und ändern Sie die spezielle Methode "eq" --Ändern Sie die Klasse "Geld", die als abstrakte Klasse behandelt wurde, unter der Annahme, dass ein "Geld" -Objekt erstellt wird

example/money.py


class Money():
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

    def currency(self):
        return self.__currency

    @classmethod
    def dollar(cls, amount):
        return Dollar(amount, "USD")

    @classmethod
    def franc(cls, amount):
        return Franc(amount, "CHF")


class Dollar(Money):
    def __init__(self, amount, currency):
        super().__init__(amount, currency)


class Franc(Money):
    def __init__(self, amount, currency):
        super().__init__(amount, currency)

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test war erwartungsgemäß erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 20%]
tests/test_money.py::MoneyTest::testDifferentClassEquality PASSED        [ 40%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 60%]
tests/test_money.py::MoneyTest::testFrancMultiplication PASSED           [ 80%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 11: Die Wurzel allen Übels

Die Klassen "Dollar" und "Franc" können ebenfalls gestrichen werden, wodurch das bisherige Refactoring abgeschlossen wird.

(1) Schreiben Sie zuerst einen kleinen Test.

Multiplizieren Sie den Betrag (Betrag pro Währungseinheit) mit der Anzahl (Anzahl der Währungseinheiten), um den Betrag zu erhalten. Unter der Annahme, dass das Refactoring des als bezeichneten Mechanismus abgeschlossen ist, wird auch der Test selbst refactored.

tests/test_money.py


from testtools import TestCase
from example.money import Money

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(Money.franc(5) == Money.dollar(5))

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

(2) Refactor.

Sie müssen die Klassen "Dollar" und "Franken" nicht definieren, da Sie bereits im Objekt "Geld" zwischen "** Dollarwährung " und " Frankenwährung **" unterscheiden können. Wird sein. Entfernen Sie die Klassen "Dollar", "Franc", um das Refactoring abzuschließen.

example/money.py


class Money():
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

    def currency(self):
        return self.__currency

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

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

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test war erwartungsgemäß erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 66%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [100%]

Kapitel 12: Design und Metapher (Ergänzung, schließlich)

Fügen Sie daraus zwei verschiedene Währungsbeträge hinzu, um den umgerechneten Betrag basierend auf dem Wechselkurs zwischen den Währungen zu erhalten. Wir werden einen Mechanismus realisieren, der die Anforderungen von `erfüllt. Darüber hinaus scheint es, dass diese Anforderung in die folgenden zwei Aufgaben zerlegt werden kann.

  • $5 + $5 = $10
  • "$ 5 + 10 CHF = $ 10 (wenn der Kurs 2: 1 ist)"

Addieren Sie zuerst die zwei Beträge, um den Betrag zu erhalten. Als Mechanismus von fahren wir mit der Codierung fort, so dass $ 5 + $ 5 = $ 10` gilt.

(1) Schreiben Sie zuerst einen kleinen Test.

Addiere zwei Beträge, um den Betrag zu erhalten. Fügen Sie den Test testSimpleAddition hinzu, um zu sehen, wie funktioniert. Darüber hinaus werden wir die folgenden zwei Konzepte für zukünftige Designs berücksichtigen.

  • Das Konzept des "Bankobjekts" basiert auf der Idee, dass es in der Verantwortung der Bank liegen sollte, die Währung umzurechnen
  • Das Konzept einer "reduzierten" Variablen zum Speichern des Umrechnungsergebnisses, das durch Anwendung des "Wechselkurses" erhalten wird

tests/test_money.py


from testtools import TestCase
from example.money import Money
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)

(2) Nehmen Sie kleine Änderungen vor.

Die in diesem Kapitel behandelte Implementierung ist ziemlich kompliziert, daher wird es eine ziemlich große Änderung sein, aber es sieht so aus.

―― Addiere zwei Beträge, um den Betrag zu erhalten. Um den Mechanismus zu implementieren, fügen Sie der Money Klasse eine Plus Methode hinzu.

  • Ein "Geld" -Objekt wird durch Erben der abstrakten Klasse "Ausdruck" erstellt
  • Die "Reduce" -Methode der "Bank" -Klasse wird hier vorläufig implementiert.

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

    def plus(self, addend):
        return Money(self.__amount + addend.__amount, self.__currency)

    def currency(self):
        return self.__currency

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

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

example/bank.py


from example.money import Money

class Bank():
    def reduce(self, source , toCurrency):
        return Money.dollar(10)

example/expression.py


from abc import ABCMeta

class Expression(metaclass=ABCMeta):
    pass

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 25%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 50%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 75%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

Kapitel 13: Mach es

Fügen Sie weiterhin zwei Beträge hinzu, um den Betrag zu erhalten. Fahren Sie als Mechanismus von mit der Codierung fort, sodass $ 5 + $ 5 = $ 10` gilt. Letztes Mal werden wir das Verhalten der "Reduce" -Methode der "Bank" -Klasse starten, wo sie als temporäre Implementierung definiert wurde. Da die Anstrengungen hier wahrscheinlich recht umfangreich sind, werden wir außerdem mit den folgenden Schritten fortfahren.

--STEP1: Schreiben Sie die Verarbeitung von $ 5 + $ 5 --STEP2: Verwirklichen Sie die vorübergehende Implementierung der "Reduce" -Methode der "Bank" -Klasse --STEP3: Verwirklichen Sie die vorübergehende Implementierung der "Reduce" -Methode der "Bank" -Klasse (Forts.)

SCHRITT 1: Schreiben Sie die Verarbeitung von $ 5 + $ 5

Als erster Schritt zur Realisierung von "$ 5 + $ 5 = $ 10" wird die Verarbeitung des Teils von "$ 5 + $ 5" durchgeführt.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testPlusReturnsSum hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)

    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Definieren Sie eine neue "Sum" -Klasse
  • Speichern Sie im Objekt "Summe" den Status von zwei Beträgen: "augend" und "addend". --Ändern Sie die Plus-Methode der Money-Klasse, um das Sum-Objekt zurückzugeben.

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def currency(self):
        return self.__currency

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

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

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

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 20%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 40%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 60%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 80%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

SCHRITT 2: Verwirklichen Sie die vorübergehende Implementierung der Reduktionsmethode der Bankklasse

Wir werden die "Reduce" -Methode der "Bank" -Klasse verwirklichen. Hier zielen wir auf das Summenobjekt.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testReduceSum hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)

    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

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

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Mit der Methode "Reduzieren" der Klasse "Bank" können Sie das Verarbeitungsergebnis der Methode "Reduzieren" des Objekts "Summe" zurückgeben.
  • Fügen Sie in der Methode "Reduzieren" der Klasse "Summe" zwei Beträge hinzu, um den Betrag zu erhalten. Definieren Sie den Mechanismus von `
  • Definieren Sie die Methode "Betrag" in der Klasse "Geld", damit das private Mitglied "__Betrag" von außen referenziert werden kann.

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def amount(self):
        return self.__amount

    def currency(self):
        return self.__currency

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

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

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

    def reduce(self, toCurrency):
        amount = self.augend.amount() + self.addend.amount()
        return Money(amount, toCurrency)

example/bank.py


class Bank():
    def reduce(self, source , toCurrency):
        return source.reduce(toCurrency)

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 16%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 33%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 50%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 66%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 83%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

SCHRITT 3: Materialisieren Sie die vorübergehende Implementierung der "Reduce" -Methode der "Bank" -Klasse (Forts.)

Wir werden die "Reduce" -Methode der "Bank" -Klasse verwirklichen. Hier zielen wir auf das Objekt "Geld".

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testReduceMoney hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Mit der Methode "Reduzieren" der Klasse "Bank" können Sie das Verarbeitungsergebnis der Methode "Reduzieren" des Objekts "Geld" zurückgeben.
  • Definieren Sie die "Reduce" -Methode der "Money" -Klasse (vorläufige Implementierung)
  • Definieren Sie die abstrakte Methode "Reduzieren" in der abstrakten Klasse "Ausdruck", um die Definition der Methode "Reduzieren" in der Klasse "Geld" und der Klasse "Summe" zu erzwingen.

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def reduce(self, toCurrency):
        return self

    def amount(self):
        return self.__amount

    def currency(self):
        return self.__currency

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

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

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

    def reduce(self, toCurrency):
        amount = self.augend.amount() + self.addend.amount()
        return Money(amount, toCurrency)

example/expression.py


from abc import ABCMeta, abstractmethod

class Expression(metaclass=ABCMeta):
    @abstractmethod
    def reduce(self, toCurrency):
        pass

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 14%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 28%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 42%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 57%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 71%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 85%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

Kapitel 14: Lern- und Regressionstests (Änderung)

Hier werden wir den Prozess der Umrechnung von 2 Franken in 1 Dollar realisieren.

--STEP1: Verarbeitung zur Umrechnung von 2 Franken in 1 Dollar (Wechselkurs vorläufig implementiert) --STEP2: Verarbeitung zur Umrechnung von 2 Franken in 1 Dollar (Implementierung der Wechselkurstabelle)

SCHRITT 1: Verarbeitung zur Umrechnung von 2 Franken in 1 Dollar (Wechselkurs vorläufig implementiert)

Wir werden den Prozess der Umrechnung von 2 Franken in 1 Dollar realisieren. Es wird jedoch davon ausgegangen, dass der Wechselkurs-> USD: CHF = 2: 1 ist.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testReduceMoneyDifferentCurrency hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

--Add_rateMethode zurBank` Klasse (temporäre Implementierung)

  • Wenn Sie die "Reduce" -Methode des "Money" -Objekts aus der "Reduce" -Methode der "Bank" -Klasse aufrufen, übergeben Sie Ihr eigenes "Bank" -Objekt.
  • Wenn Sie die "Reduce" -Methode des "Sum" -Objekts aus der "Reduce" -Methode der "Bank" -Klasse aufrufen, übergeben Sie Ihr eigenes "Bank" -Objekt.
  • Definieren Sie die "Kurs" -Methode der "Bank" -Klasse, damit der Wechselkurs vorläufig ermittelt werden kann (Wechselkurs-> USD: CHF = 2: 1).

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def reduce(self, bank, toCurrency):
        rate = bank.rate(self.__currency, toCurrency)
        return Money(self.__amount / rate, toCurrency)

    def amount(self):
        return self.__amount

    def currency(self):
        return self.__currency

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

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

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

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

example/bank.py


class Bank():
    def reduce(self, source , toCurrency):
        return source.reduce(self, toCurrency)

    def add_rate(self, fromCurrency, toCurrency, rate):
        pass

    def rate(self, fromCurrency, toCurrency):
        return 2 if (fromCurrency == "CHF" and toCurrency == "USD") else 1

example/expression.py


from abc import ABCMeta, abstractmethod

class Expression(metaclass=ABCMeta):
    @abstractmethod
    def reduce(self, bank, toCurrency):
        pass

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 12%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 25%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 37%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 50%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 62%]
tests/test_money.py::MoneyTest::testReduceMoneyDifferentCurrency PASSED  [ 75%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 87%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

SCHRITT 2: Verarbeitung zur Umrechnung von 2 Franken in 1 Dollar (Implementierung der Wechselkurstabelle)

Basierend auf der Wechselkurstabelle werden wir den Prozess der Umrechnung von 2 Franken in 1 Dollar realisieren.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testIdentityRate hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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"))

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Ermöglichen Sie die Pflege der Wechselkurstabelle in der Klasse "Bank"
  • Verschiedene Wechselkurse können der Wechselkurstabelle mit der Methode "add_rate" des Objekts "Bank" hinzugefügt werden.
  • Sie können auf den erforderlichen Kurs mit der Methode "Rate" des Objekts "Bank" verweisen.

example/bank.py


class Bank():
    def __init__(self):
        self._rates = {}

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

    def add_rate(self, fromCurrency, toCurrency, rate):
        target_rate = "{0}:{1}".format(fromCurrency, toCurrency)
        self._rates[target_rate] = rate

    def rate(self, fromCurrency, toCurrency):
        target_rate = "{0}:{1}".format(fromCurrency, toCurrency)
        if fromCurrency == toCurrency:
            return 1
        return self._rates.get(target_rate)

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 11%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 22%]
tests/test_money.py::MoneyTest::testIdentityRate PASSED                  [ 33%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 44%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 55%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 66%]
tests/test_money.py::MoneyTest::testReduceMoneyDifferentCurrency PASSED  [ 77%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 88%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

Kapitel 15: Test und Compiler (gemischte Währungen)

Fügen Sie bisher zwei verschiedene Währungsbeträge hinzu und erhalten Sie den umgerechneten Betrag basierend auf dem Wechselkurs zwischen den Währungen. Wir haben uns zum Ziel gesetzt, einen Mechanismus zu realisieren, der die Anforderungen von `erfüllt. Hier beginnen wir mit der Aufgabe "5 $ + 10 CHF = 10 $ (bei einer Rate von 2: 1)".

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testMixedAddition hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        result = bank.reduce(fiveBucks.plus(tenFrancs), "USD")
        self.assertEqual(Money.dollar(10), result)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Ändern Sie die Ableitungsmethode "Betrag" in der Methode "Reduzieren" des Objekts "Summe"
  • Definieren Sie die Plus-Methode in der Sum-Klasse
  • Definieren Sie die abstrakte Methode "plus" in der abstrakten Klasse "Expression" und erzwingen Sie die Definition der "plus" -Methode in der Klasse "Money" und der Klasse "Sum".

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def reduce(self, bank, toCurrency):
        rate = bank.rate(self.__currency, toCurrency)
        return Money(self.__amount / rate, toCurrency)

    def amount(self):
        return self.__amount

    def currency(self):
        return self.__currency

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

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

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

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

    def plus(self, addend):
        pass

example/expression.py


from abc import ABCMeta, abstractmethod

class Expression(metaclass=ABCMeta):
    @abstractmethod
    def plus(self, addend):
        pass

    @abstractmethod
    def reduce(self, bank, toCurrency):
        pass

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [ 10%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 20%]
tests/test_money.py::MoneyTest::testIdentityRate PASSED                  [ 30%]
tests/test_money.py::MoneyTest::testMixedAddition PASSED                 [ 40%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 50%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 60%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 70%]
tests/test_money.py::MoneyTest::testReduceMoneyDifferentCurrency PASSED  [ 80%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 90%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [100%]

Kapitel 16: Testen für zukünftige Leser (Abstraktion, endlich)

Schließlich addieren Sie die zwei verschiedenen Währungen und erhalten den umgerechneten Betrag basierend auf dem Wechselkurs zwischen den Währungen. Wir werden die Realisierung eines Mechanismus abschließen, der die Anforderungen von `erfüllt.

--STEP1: Beende die Plus-Methode der Sum-Klasse --STEP2: Beende die "times" -Methode der "Sum" -Klasse

SCHRITT 1: Schließen Sie die Plus-Methode der Sum-Klasse ab

Vervollständige die Plus-Methode der Sum-Klasse.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testSumPlusMoney hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        result = bank.reduce(fiveBucks.plus(tenFrancs), "USD")
        self.assertEqual(Money.dollar(10), result)

    def testSumPlusMoney(self):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        _sum = Sum(fiveBucks, tenFrancs).plus(fiveBucks)
        result = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(15), result)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Gibt das "Sum" -Objekt zurück, wenn die "Plus" -Methode des "Sum" -Objekts aufgerufen wird.
  • Definieren Sie die abstrakte Methode "plus" in der abstrakten Klasse "Expression" und erzwingen Sie die Definition der "plus" -Methode in der Klasse "Money" und der Klasse "Sum".

example/money.py


from example.expression import Expression

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

    def __eq__(self, other):
        return (self.amount == other.amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.amount * multiplier, self._currency)

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

    def reduce(self, bank, toCurrency):
        rate = bank.rate(self.currency(), toCurrency)
        return Money(self.amount / rate, toCurrency)

    def currency(self):
        return self._currency

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

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

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

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

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

example/bank.py


class Bank():
    def __init__(self):
        self._rates = {}

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

    def add_rate(self, fromCurrency, toCurrency, rate):
        self._rates[(fromCurrency, toCurrency)] = rate

    def rate(self, fromCurrency, toCurrency):
        if fromCurrency == toCurrency:
            return 1
        return self._rates.get((fromCurrency, toCurrency))

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Der Test ist erfolgreich.

$ pytest -v

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [  9%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 18%]
tests/test_money.py::MoneyTest::testIdentityRate PASSED                  [ 27%]
tests/test_money.py::MoneyTest::testMixedAddition PASSED                 [ 36%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 45%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 54%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 63%]
tests/test_money.py::MoneyTest::testReduceMoneyDifferentCurrency PASSED  [ 72%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 81%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [ 90%]
tests/test_money.py::MoneyTest::testSumPlusMoney PASSED                  [100%]

SCHRITT 2: Schließen Sie die "Times" -Methode der "Sum" -Klasse ab

Vervollständige die times Methode der Sum Klasse.

(1) Schreiben Sie zuerst einen kleinen Test.

Fügen Sie den Test testSumTimes hinzu.

tests/test_money.py


from testtools import TestCase
from example.money import Money, Sum
from example.bank import Bank

class MoneyTest(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 testEquality(self):
        self.assertTrue(Money.dollar(5) == Money.dollar(5))
        self.assertFalse(Money.dollar(5) == Money.dollar(6))
        self.assertFalse(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.plus(five)
        bank = Bank()
        reduced = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(10), reduced)
    
    def testPlusReturnsSum(self):
        five = Money.dollar(5)
        _sum = five.plus(five)
        self.assertEqual(five, _sum.augend)
        self.assertEqual(five, _sum.addend)

    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):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        result = bank.reduce(fiveBucks.plus(tenFrancs), "USD")
        self.assertEqual(Money.dollar(10), result)

    def testSumPlusMoney(self):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        _sum = Sum(fiveBucks, tenFrancs).plus(fiveBucks)
        result = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(15), result)

    def testSumTimes(self):
        fiveBucks = Money.dollar(5)
        tenFrancs = Money.franc(10)
        bank = Bank()
        bank.add_rate("CHF", "USD", 2)
        _sum = Sum(fiveBucks, tenFrancs).times(2)
        result = bank.reduce(_sum, "USD")
        self.assertEqual(Money.dollar(20), result)

(2) Nehmen Sie kleine Änderungen vor.

Nehmen Sie folgende Änderungen vor:

  • Geben Sie das "Sum" -Objekt zurück, wenn die "times" -Methode des "Sum" -Objekts aufgerufen wird
  • Definieren Sie die abstrakte Methode "times" in der abstrakten Klasse "Expression" und erzwingen Sie die Definition der "times" -Methode in der Klasse "Money" und der Klasse "Sum".

example/money.py


from example.expression import Expression

class Money(Expression):
    def __init__(self, amount, currency):  
        self.__amount = amount
        self.__currency = currency

    def __eq__(self, other):
        return (self.__amount == other.__amount
                and self.currency() == other.currency())

    def times(self, multiplier):
        return Money(self.__amount * multiplier, self.__currency)

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

    def reduce(self, bank, toCurrency):
        rate = bank.rate(self.__currency, toCurrency)
        return Money(self.__amount / rate, toCurrency)

    def amount(self):
        return self.__amount

    def currency(self):
        return self.__currency

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

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

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

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

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

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

example/expression.py


from abc import ABCMeta, abstractmethod

class Expression(metaclass=ABCMeta):
    @abstractmethod
    def plus(self, addend):
        pass

    @abstractmethod
    def reduce(self, bank, toCurrency):
        pass

    @abstractmethod
    def times(self, multiplier):
        pass

(3) Führen Sie den Test durch und stellen Sie sicher, dass alles erfolgreich ist. -> OK </ font>

Die Tests waren erfolgreich und die Abdeckung ist im Allgemeinen gut.

$ pytest -v --cov=example

...(snip)
tests/test_money.py::MoneyTest::testCurrency PASSED                      [  8%]
tests/test_money.py::MoneyTest::testEquality PASSED                      [ 16%]
tests/test_money.py::MoneyTest::testIdentityRate PASSED                  [ 25%]
tests/test_money.py::MoneyTest::testMixedAddition PASSED                 [ 33%]
tests/test_money.py::MoneyTest::testMultiplication PASSED                [ 41%]
tests/test_money.py::MoneyTest::testPlusReturnsSum PASSED                [ 50%]
tests/test_money.py::MoneyTest::testReduceMoney PASSED                   [ 58%]
tests/test_money.py::MoneyTest::testReduceMoneyDifferentCurrency PASSED  [ 66%]
tests/test_money.py::MoneyTest::testReduceSum PASSED                     [ 75%]
tests/test_money.py::MoneyTest::testSimpleAddition PASSED                [ 83%]
tests/test_money.py::MoneyTest::testSumPlusMoney PASSED                  [ 91%]
tests/test_money.py::MoneyTest::testSumTimes PASSED                      [100%]

...(snip)
---------- coverage: platform darwin, python 3.8.0-final-0 -----------
Name                    Stmts   Miss  Cover
-------------------------------------------
example/__init__.py         0      0   100%
example/bank.py            13      0   100%
example/expression.py      11      3    73%
example/money.py           35      0   100%
-------------------------------------------
TOTAL                      59      3    95%

...(snip)

Mit dem oben Gesagten habe ich versucht, die Entwicklung der Python-Version von "multinationaler Währung" unter Verwendung von ** testgetriebener Entwicklung ** zu erleben.

■ Am Ende ...

Sie können die testgetriebene Entwicklung in Python auch in Teil II "xUnit" des Buches "Testgetriebene Entwicklung" erleben. Als ich es erlebte, bemerkte ich tatsächlich, dass ** testgetriebene Entwicklung ** ganz anders war als der Test, den ich mir vorgestellt hatte, und ich habe es völlig falsch verstanden. Dies ist ein Zitat aus Kapitel 32 des Buches "Learn TDD"! !!

Ironischerweise ist TDD keine Testtechnik (Cunninghams Vorschlag). TDD ist eine Analysetechnik, eine Entwurfstechnik und tatsächlich eine Technik, die alle Aktivitäten der Entwicklung strukturiert.

Anhang C, "Übersetzerkommentar: Die Gegenwart der testgetriebenen Entwicklung", half mir, TDD / BDD zu verstehen, und war eine großartige Lernerfahrung.

■ Referenz-URL

Recommended Posts