A module is a file containing Python definitions and statements.The Python Tutorial, https://docs.python.org/3/tutorial/modules.html
In this chapter, we’ll do a few things to improve the organization of our Python code. We’ll separate our test code from our production code using modules. We’ll see how the scoping and import rules in Python help us ensure the dependencies in our code are correct. Finally, we’ll remove a redundant test from our code, making things compact and meaningful.
We have production code for Money
and Portfolio
right next to our test code in the same file. We need to separate them into individual source files.
Let’s first create two new files named money.py
and portfolio.py
in the same folder as test_money.py
. Our folder structure looks as shown below:
py ├── money.py ├── portfolio.py └── test_money.py
We move the code for Money
and Portfolio
classes to money.py
and portfolio.py
, respectively. The code segment below shows the complete contents of portfolio.py
after this code relocation:
import
functools
import
operator
class
Portfolio
:
def
__init__
(
self
):
self
.
moneys
=
[]
def
add
(
self
,
*
moneys
):
self
.
moneys
.
extend
(
moneys
)
def
evaluate
(
self
,
currency
):
total
=
functools
.
reduce
(
operator
.
add
,
map
(
lambda
m
:
m
.
amount
,
self
.
moneys
),
0
)
return
Money
(
total
,
currency
)
Notice that we carry the two import
statements along with the code for the Portfolio
class, because Portfolio
uses functools
and operator
.
The file money.py
, not shown here, similarly contains the Money
class and its methods.
When we run our tests now, we get our old friend, NameError
arising from our tests:
File
"/Users/saleemsiddiqui/code/github/saleem/tdd-project/py/test_money.py"
,
line
21
,
in
testAddition
fiveDollars
=
Money
(
5
,
"USD"
)
NameError
:
name
'Money'
is
not
defined
We realize that the test class is dependent on both Money
and Portfolio
, so we add these import
statements near at the top of test_money.py
:
from
money
import
Money
from
portfolio
import
Portfolio
Ah: we now get NameError: name 'Money' is not defined
from within Portfolio
! A quick look at portfolio.py
shows that it depends on Money
, too. So we add from money import Money
to the top of portfolio.py
and all tests become green. Yay!
Moving code around and adding import
statements makes the dependency tree of our code clearer. Figure 7-1 shows the dependency diagram of our code.
We currently have two tests for multiplication, and one each for division and addition. The two tests for multiplication test the same functionality in the Money
class. This is a bit of duplication we can do without. Let’s delete the testMultiplicationInDollars
and rename the other test to simply testMultiplication
. The resulting symmetry — three tests for the three features (Multiplication, Division, and Addition) where each test uses a different currency (Euros, Wons, and Dollars respectively) — is both compact and elegant.
We have added a couple of new files and partitioned code among them. This is an especially opportune moment to commit our changes to our local Git repository.
git add .
git commit -m "refactor: moved Money and Portfolio classes their own Python files"
The output of these two commands should validate our changes:
[
main c917e7c]
refactor: moved Money and Portfolio classes their own Python files3
files changed,30
insertions(
+)
,33
deletions(
-)
create mode100644
py/money.py create mode100644
py/portfolio.py
In this chapter, we separated Money
and Portfolio
into their own source files, which in Python, makes them their own modules. The separation ensured that the dependency from test code to production code was explicit and that there is no dependency in the other direction.
We also removed an extraneous test, thereby simplifying our code.
3.144.212.145