Followed fast and followed faster
Edgar Allen Poe, The Raven
Did the RED-GREEN-REFACTOR cycle we followed in Chapter 1 seem a tad too slow?
A response of “heck yes!” (or some rhyming phrase) is understandable!
The goal of test-driven development isn’t to force us to go slow. Or fast, for that matter. Its goal is to allow us to go at a pace we’re comfortable with: speeding up when we can, slowing down when we should.
In this chapter, we’ll introduce additional currencies and the ability to multiply and divide money in any currency. Let’s see if we can kick up the pace a bit.
The second item on our list of features introduces a new currency:
5 USD x 2 = 10 USD |
10 EUR x 2 = 20 EUR |
4002 KRW / 4 = 1000.5 KRW |
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
This indicates that we need a more general entity than the Dollar
we created in the previous chapter. Something like Money
, which encapsulates an amount
(which we already have) and a currency
(which we do not yet have). Let’s write tests to flush out this new feature.
Let’s write a new test in money_test.go
. This test requires that when a struct representing “10 Euros” is multiplied by 2, we get “20 Euros”:
func
TestMultiplicationInEuros
(
t
*
testing
.
T
)
{
tenEuros
:=
Money
{
amount
:
10
,
currency
:
"EUR"
}
twentyEuros
:=
tenEuros
.
Times
(
2
)
if
twentyEuros
.
amount
!=
20
{
t
.
Errorf
(
"Expected 20, got: [%d]"
,
twentyEuros
.
amount
)
}
if
twentyEuros
.
currency
!=
"EUR"
{
t
.
Errorf
(
"Expected EUR, got: [%s]"
,
twentyEuros
.
currency
)
}
}
The test expresses the concepts of “10 Euros” and “20 Euros” with struct
instances containing a currency
as well as amount
.
By now, we know that when we run this test, we’ll get an error notifying us of undefined: Money
. We can eliminate this by introducing a new struct:
type
Money
struct
{
amount
int
currency
string
}
We now get the error type Money has no field or method Times
, which we know how to get around. We define a Times
method for Money
:
func
(
m
Money
)
Times
(
multiplier
int
)
Money
{
return
Money
{
amount
:
m
.
amount
*
multiplier
,
currency
:
m
.
currency
}
}
Yay! Green tests again.
Let’s write a test for representing a Money
object with amount
and currency
. We verify that when an object representing “10 Euros” is multiplied by 2, we get “20 Euros”. We define this test at the very end of test_money.js
:
tenEuros
=
new
Money
(
10
,
"EUR"
);
twentyEuros
=
tenEuros
.
times
(
2
);
assert
.
strictEqual
(
twentyEuros
.
amount
,
20
);
assert
.
strictEqual
(
twentyEuros
.
currency
,
"EUR"
);
By now, we anticipate the ReferenceError: Money is not defined
error we get when running the tests. We can eliminate this by introducing a new class named Money
with the minimally desired behavior, that is: a constructor
that initializes both amount
and currency
, and a times
method that multiplies the amount
with a given multiplier
and returns a new Money
object.
class
Money
{
constructor
(
amount
,
currency
)
{
this
.
amount
=
amount
;
this
.
currency
=
currency
;
}
times
(
multiplier
)
{
return
new
Money
(
this
.
amount
*
multiplier
,
this
.
currency
);
}
}
Yay! Both our tests are now green.
Let’s add a new test in the TestMoney
class. This test would verify that multiplying an object representing “10 Euros” by 2 gives us an object representing “20 Euros”:
def
testMultiplicationInEuros
(
self
):
tenEuros
=
Money
(
10
,
"EUR"
)
twentyEuros
=
tenEuros
.
times
(
2
)
self
.
assertEqual
(
20
,
twentyEuros
.
amount
)
self
.
assertEqual
(
"EUR"
,
twentyEuros
.
currency
)
By now, we anticipate the NameError: name 'Money' is not defined
error we get when we run the tests. We can get rid of this error by introducing a new Money
class with the minimally necessary behavior. This means an __init__
method that initializes both amount
and currency
, and a times
method that returns a new Money
object whose amount
is a product of the multiplier
and the amount
of the original Money
object:
class
Money
:
def
__init__
(
self
,
amount
,
currency
):
self
.
amount
=
amount
self
.
currency
=
currency
def
times
(
self
,
multiplier
):
return
Money
(
self
.
amount
*
multiplier
,
self
.
currency
)
Yay! Both our tests are now green.
Wait a minute: didn’t we just create a horrendous duplication in our code? The new entity we created to represent Money
subsumes what we wrote earlier for Dollar
. This can’t possibly be good. A oft-quoted rule in writing code is the DRY principle:
Don’t
Repeat
Yourself.
Recall the RED-GREEN-REFACTOR cycle. What we did in the previous section got us to green, but we haven’t done the necessary refactoring yet. Let’s remove the duplication in the code while keeping our tests green.
We realize that the Money
struct can do everything that the Dollar
struct can, and more. Money
has currency
, which Dollar
does not.
Let’s delete the Dollar
struct and its Times
method.
When we do this, the TestMultiplication
test predictably breaks with an undefined: Dollar
error. Let’s refactor that test to use Money
instead:
func
TestMultiplicationInDollars
(
t
*
testing
.
T
)
{
fiver
:=
Money
{
amount
:
5
,
currency
:
"USD"
}
tenner
:=
fiver
.
Times
(
2
)
if
tenner
.
amount
!=
10
{
t
.
Errorf
(
"Expected 10, got: [%d]"
,
tenner
.
amount
)
}
if
tenner
.
currency
!=
"USD"
{
t
.
Errorf
(
"Expected USD, got: [%s]"
,
tenner
.
currency
)
}
}
Both tests now pass. Notice that we renamed the test to be more descriptive: TestMultiplicationInDollars
.
The Money
class can do everything that Dollar
does, and more. This means that we can delete the Dollar
class in its entirety.
When we do this and run the tests, we get our old friendly error: ReferenceError: Dollar is not defined
. Let’s refactor the first test to use Money
instead:
fiver
=
new
Money
(
5
,
"USD"
);
tenner
=
fiver
.
times
(
2
);
assert
.
strictEqual
(
tenner
.
amount
,
10
);
assert
.
strictEqual
(
tenner
.
currency
,
"USD"
);
Both tests now pass.
The Money
class’s functionality is a superset of that of the Dollar
class. Which means we don’t need the latter. Let’s delete the Dollar
class in its entirety.
Having done this, we get the familiar NameError: name 'Dollar' is not defined
message when we run the tests. Let’s refactor the first test to use Money
instead of the erstwhile Dollar
:
def
testMultiplicationInDollars
(
self
):
fiver
=
Money
(
5
,
"USD"
)
tenner
=
fiver
.
times
(
2
)
self
.
assertEqual
(
10
,
tenner
.
amount
)
self
.
assertEqual
(
"USD"
,
tenner
.
currency
)
Both tests now pass. Notice that we renamed the test to be more descriptive: testMultiplicationInDollars
.
Hmm. The two tests — the one for Dollars and the one for Euros — are very similar. The currencies and amounts vary, but they test pretty much the same feature.
Repetition in code comes in varied forms. Sometimes we have identical lines of code (perhaps caused by “copy pasta” programming). In these cases, extracting the common lines to a function or method is what we need to do. At other times, we have parts of code that are not identical, but conceptually similar. This is the case with our two tests.
We could delete one of the tests and still feel confident about our code. However, we also want to safeguard ourselves against accidental regression in our code. Recall that our very first implementation used hard-coded numbers (10
or 5 * 2
). Having two distinct tests with different values ensures that we won’t accidentally go back to that naive implementation.
Regression — “a return to a primitive or less developed state" — is a common theme in writing software. Having a battery of tests is a reliable way to ensure that we don’t break existing features as we build new ones.
Let’s keep both test cases for now. We’ll add an item to the end of our checklist noting our desire to remove redundancy in tests. We’ll revisit this item later, after we address division.
Here’s our feature list:
5 USD x 2 = 10 USD |
10 EUR x 2 = 20 EUR |
4002 KRW / 4 = 1000.5 KRW |
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
Remove redundant |
The next requirement is to allow division. On the surface, it looks very similar to multiplication. We know from elementary mathematics that dividing by x is the same as multiplying by 1/x. 1
Let’s test-drive this new feature and see how our code evolves. By now, we are getting into the groove of starting with a failing test. As an indicator of our growing confidence, we’ll introduce two new things in our test:
A new currency: Korean Won (KRW), and
Numbers with fractional parts, e.g. 1000.5
Let’s write our new test for division.
func
TestDivision
(
t
*
testing
.
T
)
{
originalMoney
:=
Money
{
amount
:
4002
,
currency
:
"KRW"
}
actualMoneyAfterDivision
:=
originalMoney
.
Divide
(
4
)
expectedMoneyAfterDivision
:=
Money
{
amount
:
1000.5
,
currency
:
"KRW"
}
if
expectedMoneyAfterDivision
!=
actualMoneyAfterDivision
{
t
.
Errorf
(
"Expected [%+v] Got: [%+v]"
,
expectedMoneyAfterDivision
,
actualMoneyAfterDivision
)
}
}
Notice that we wrote this test a bit differently. We define variables for the two structs: actualMoneyAfterDivision
and expectedMoneyAfterDivision
. And instead of comparing amount
and currency
separately, we compare the two structs as a whole. If the structs don’t match, we print them both.
In Go, printing a struct with the %+v
format “verb” prints the struct’s field names as well as values.
We anticipate the type Money has no field or method Divide
error we get when we run this test. Let’s define this missing method, taking our cue from the existing Times
method:
func
(
m
Money
)
Divide
(
divisor
int
)
Money
{
return
Money
{
amount
:
m
.
amount
/
divisor
,
currency
:
m
.
currency
}
}
Ah! The test fails with a new error: constant 1000.5 truncated to integer
.
It’s clear that we need to change the amount
field in the Money
struct so that it can hold fractional values. The float64
data type is appropriate for this purpose:
type
Money
struct
{
amount
float64
currency
string
}
This gives us new errors when we run our test:
...
invalid
operation
:
m
.
amount
*
multiplier
(
mismatched
types
float64
and
int
)
...
invalid
operation
:
m
.
amount
/
divisor
(
mismatched
types
float64
and
int
)
Using an IDE can be useful because it flags syntax errors and type errors without any need to run the tests.
We need to modify our arithmetic operations (multiplication and division) to use the same data type for all operands. We know from our domain that the multipliers and divisors are likely to be integers (number of shares of a stock) whereas the amount can be a fractional number (trading price of a particular stock). Let’s use this knowledge to convert the multiplier
and divisor
to float64
before using them in our arithmetic operations. We can do this by calling the float64()
function:
func
(
m
Money
)
Times
(
multiplier
int
)
Money
{
return
Money
{
amount
:
m
.
amount
*
float64
(
multiplier
),
currency
:
m
.
currency
}
}
func
(
m
Money
)
Divide
(
divisor
int
)
Money
{
return
Money
{
amount
:
m
.
amount
/
float64
(
divisor
),
currency
:
m
.
currency
}
}
Now we get different wrong type
failures
...
Errorf
format
%
d
has
arg
tenner
.
amount
of
wrong
type
float64
...
Errorf
format
%
d
has
arg
twentyEuros
.
amount
of
wrong
type
float64
A careful reading of the error messages reveals that we are using the wrong format “verb” in our earlier tests to print the amount
field. Since our newest test — TestDivision
— successfully compares entire struct`s, we can refactor our earlier two multiplication tests to do something similar. This way, we'll side-step the whole issue of having used the incorrect formatting "verb" for `float64
type.
Here’s how TestMultiplicationInDollars
looks after changing its assertion statement. (The other test, TestMultiplicationInEuros
, needs similar changes.)
func
TestMultiplicationInDollars
(
t
*
testing
.
T
)
{
fiver
:=
Money
{
amount
:
5
,
currency
:
"USD"
}
actualResult
:=
fiver
.
Times
(
2
)
expectedResult
:=
Money
{
amount
:
10
,
currency
:
"USD"
}
if
actualResult
!=
actualResult
{
t
.
Errorf
(
"Expected [%+v], got: [%+v]"
,
expectedResult
,
actualResult
)
}
}
If compilation or assertion failures crop up during a test run, pay attention to the error messages.
After these changes, all our tests are green.
Let’s write our new test for division at the end of test_money.js
.
originalMoney
=
new
Money
(
4002
,
"KRW"
)
actualMoneyAfterDivision
=
originalMoney
.
divide
(
4
)
expectedMoneyAfterDivision
=
new
Money
(
1000.5
,
"KRW"
)
assert
.
deepStrictEqual
(
actualMoneyAfterDivision
,
expectedMoneyAfterDivision
)
Notice that we wrote this test a bit differently. We define variables for the two objects: actualMoneyAfterDivision
and expectedMoneyAfterDivision
. And instead of comparing amount
and currency
separately, we compare the two objects at once using the deepStrictEqual
method in assert
.
In Node.js’s assert
module, the deepStrictEqual
method compares two objects and their child objects for equality using the JavaScript === operator. 2
We anticipate the TypeError: originalMoney.divide is not a function
error we get when we run this test. So let’s define this missing method, taking inspiration from the existing times
method. 3
class
Money
{
...
divide
(
divisor
)
{
return
new
Money
(
this
.
amount
/
divisor
,
this
.
currency
);
}
}
Yay! The tests are all green. JavaScript’s dynamic types make implementing this feature easier than languages with static typing.
Let’s write our new test for division in class TestMoney
:
def
testDivision
(
self
):
originalMoney
=
Money
(
4002
,
"KRW"
)
actualMoneyAfterDivision
=
originalMoney
.
divide
(
4
)
expectedMoneyAfterDivision
=
Money
(
1000.5
,
"KRW"
)
self
.
assertEqual
(
expectedMoneyAfterDivision
.
amount
,
actualMoneyAfterDivision
.
amount
)
self
.
assertEqual
(
expectedMoneyAfterDivision
.
currency
,
actualMoneyAfterDivision
.
currency
)
Notice that we wrote this test a bit differently. We define variables for the two objects: actualMoneyAfterDivision
and expectedMoneyAfterDivision
.
We anticipate the AttributeError: 'Money' object has no attribute 'divide'
message we get when we run this test. So let’s define this missing method, taking our cue from the existing times
method: 4
def
divide
(
self
,
divisor
):
return
Money
(
self
.
amount
/
divisor
,
self
.
currency
)
Yay! The tests are green. Python is a dynamically (and strongly) typed language. This makes implementing this feature easier than languages with static typing.
Let’s finish off this chapter with a bit of house cleaning, while keeping the tests green.
We now have three tests with three assertion blocks, each of which is a 3-line if
block. Except for the variable names in each test, the if
blocks are identical. This duplication is ripe for removal via extracted into a helper function, which we can call assertEqual
.
“Extract method” or “Extract function” is a common refactoring. It involves replacing common blocks of code with a call to a new function/method that encapsulates the block of code once.
func
assertEqual
(
t
*
testing
.
T
,
expected
Money
,
actual
Money
)
{
if
expected
!=
actual
{
t
.
Errorf
(
"Expected [%+v] Got: [%+v]"
,
expected
,
actual
)
}
}
The function’s body is identical to any of the three existing if
blocks. We can now call this function from each of the three tests. The TestDivision
function is shown below:
func
TestDivision
(
t
*
testing
.
T
)
{
originalMoney
:=
Money
{
amount
:
4002
,
currency
:
"KRW"
}
actualResult
:=
originalMoney
.
Divide
(
4
)
expectedResult
:=
Money
{
amount
:
1000.5
,
currency
:
"KRW"
}
assertEqual
(
t
,
expectedResult
,
actualResult
)
}
We can modify the TestMultiplicationInEuros
and TestMultiplicationInDollars
tests similarly.
The assertion using deepStrictEqual
that we used for our last test is elegant: it compares the two objects (expected value and actual value) at once. We can use it for our two other tests.
While we’re doing it, we can also address a subtle assumption in these two lines of code in our tests:
tenner
=
fiver
.
times
(
2
);
...
twentyEuros
=
tenEuros
.
times
(
2
);
From the perspective of the test, it is a bit presumptuous to assume that multiplying 5 Dollars or 10 Euros by two will yield 10 Dollars or 20 Euros, respectively. Indeed, that’s the very thing the tests purport to verify. We can improve our tests by inlining the call to the times
method, thereby saving ourselves the trouble of naming the variable:
fiveDollars
=
new
Money
(
5
,
"USD"
);
tenDollars
=
new
Money
(
10
,
"USD"
);
assert
.
deepStrictEqual
(
fiveDollars
.
times
(
2
),
tenDollars
);
“Inline variable” is a refactoring that replaces a named variable with an directly evaluated (usually anonymous) variable.
We can refactor the test for multiplying Euros similarly.
Comparing two Money
objects piecemeal is verbose and tedious. In our tests, we verify that the amount
and currency
fields of Money
objects are equal, over and over. Wouldn’t it be nice to be able to compare two Money
objects directly in a single line of code?
In Python, object equality is ultimately resolved by an invocation of the __eq__
method. By default, this method returns true if the two object references being compared in fact point to the same object. This is a very strict definition of equality: it means that an object is only equal to itself, not any other object, even of the two objects have the same state.
The default implementation of __eq__
method means that in Python, two object references are equal if and only if they point to the same object. That is: https://docs.python.org/3/reference/datamodel.html#object.eq[equality is determined by reference, not by value].
Fortunately, it is not only possible but recommended to override the __eq__
method when needed. Let us explicitly override this method within the definition of our Money
class:
class
Money
:
...
def
__eq__
(
self
,
other
):
return
self
.
amount
==
other
.
amount
and
self
.
currency
==
other
.
currency
After defining the __eq__
method, we can compare Money
objects in a single line.
While we’re refactoring, we can also address a subtle assumption implicit in how we named a couple of variables in our tests:
tenner
=
fiver
.
times
(
2
)
...
twentyEuros
=
tenEuros
.
times
(
2
)
From the test’s perspective, it isn’t a given that multiplying 5 Dollars or 10 Euros by two will yield 10 Dollars or 20 Euros, respectively. Indeed, that’s the very thing the test exists to validate. We can improve our tests by doing an inline-variable refactoring, along with the single-line assertion that we can now write.
Here’s the complete testMultiplicationInDollars
:
def
testMultiplicationInDollars
(
self
):
fiveDollars
=
Money
(
5
,
"USD"
)
tenDollars
=
Money
(
10
,
"USD"
)
self
.
assertEqual
(
tenDollars
,
fiveDollars
.
times
(
2
))
We initialize fiveDollars
and tenDollars
explicitly. We then verify that multiplying the former by 2 yields an object that’s equal to the latter. We also do it in one line, keeping our code readable and succinct.
The other two tests can be refactored similarly.
We have written a couple more tests and the associated code to make them green. Time to commit these changes to our local Git repository.
git
add
.
git
commit
-m
"feat: division and multiplication features done"
Add all files, including all changes in them, to the Git index
Commit the Git index to the repository with the given message
At this point, we have two commits in our Git history, a fact we can verify by examining the output of git log
:
commit
1e43b6e6731407a810601d973c83b406249f4d59
(
HEAD
->
main
)
Author:
Saleem
Siddiqui
...
Date:
Sun
Mar
7
12:58:47
2021
-0600
feat:
division
and
multiplication
features
done
commit
bb31b94e90029ddeeee89f3ca0fe099ea7556603
Author:
Saleem
Siddiqui
...
Date:
Sun
Mar
7
12:26:06
2021
-0600
chore:
first
green
test
New SHA hash for our second commit, which represents the HEAD of the Git repository
The message we used for our second commit
[.small]#The SHA hash for our previous commit from Chapter 1 #
In this chapter, we built a second feature, division, and modified our design to deal with numbers with fractions. We have introduced a Money
entity that allows us to consolidate how Dollars and Euros (and potentially other currencies) are multiplied by a number. We have a couple of passing tests. We have also cleaned up our code along the way.
Depending on the specific data types and language, floating point arithmetic can cause problems of overflow/underflow. If needed, the problems can be surfaced via tests, and then solved — using RGR cycle. We also refactored our code to make it succinct and expressive.
With a couple more features crossed off our list, we’re ready to look at adding up amounts in different currencies — which will get our attention in the next chapter.
Here’s where we are in our feature list:
5 USD x 2 = 10 USD |
10 EUR x 2 = 20 EUR |
4002 KRW / 4 = 1000.5 KRW |
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
Remove redundant |
Here’s how the file money_test.go
looks right now:
package
main
import
(
"testing"
)
func
TestMultiplicationInDollars
(
t
*
testing
.
T
)
{
fiver
:=
Money
{
amount
:
5
,
currency
:
"USD"
}
actualResult
:=
fiver
.
Times
(
2
)
expectedResult
:=
Money
{
amount
:
10
,
currency
:
"USD"
}
assertEqual
(
t
,
expectedResult
,
actualResult
)
}
func
TestMultiplicationInEuros
(
t
*
testing
.
T
)
{
tenEuros
:=
Money
{
amount
:
10
,
currency
:
"EUR"
}
actualResult
:=
tenEuros
.
Times
(
2
)
expectedResult
:=
Money
{
amount
:
20
,
currency
:
"EUR"
}
assertEqual
(
t
,
expectedResult
,
actualResult
)
}
func
TestDivision
(
t
*
testing
.
T
)
{
originalMoney
:=
Money
{
amount
:
4002
,
currency
:
"KRW"
}
actualResult
:=
originalMoney
.
Divide
(
4
)
expectedResult
:=
Money
{
amount
:
1000.5
,
currency
:
"KRW"
}
assertEqual
(
t
,
expectedResult
,
actualResult
)
}
func
assertEqual
(
t
*
testing
.
T
,
expected
Money
,
actual
Money
)
{
if
expected
!=
actual
{
t
.
Errorf
(
"Expected [%+v] Got: [%+v]"
,
expected
,
actual
)
}
}
type
Money
struct
{
amount
float64
currency
string
}
func
(
m
Money
)
Times
(
multiplier
int
)
Money
{
return
Money
{
amount
:
m
.
amount
*
float64
(
multiplier
),
currency
:
m
.
currency
}
}
func
(
m
Money
)
Divide
(
divisor
int
)
Money
{
return
Money
{
amount
:
m
.
amount
/
float64
(
divisor
),
currency
:
m
.
currency
}
}
Here’s how the test_money.js
file looks at this point:
const
assert
=
require
(
'assert'
);
class
Money
{
constructor
(
amount
,
currency
)
{
this
.
amount
=
amount
;
this
.
currency
=
currency
;
}
times
(
multiplier
)
{
return
new
Money
(
this
.
amount
*
multiplier
,
this
.
currency
);
}
divide
(
divisor
)
{
return
new
Money
(
this
.
amount
/
divisor
,
this
.
currency
);
}
}
fiveDollars
=
new
Money
(
5
,
"USD"
);
tenDollars
=
new
Money
(
10
,
"USD"
);
assert
.
deepStrictEqual
(
fiveDollars
.
times
(
2
),
tenDollars
);
tenEuros
=
new
Money
(
10
,
"EUR"
);
twentyEuros
=
new
Money
(
20
,
"EUR"
);
assert
.
deepStrictEqual
(
tenEuros
.
times
(
2
),
twentyEuros
);
originalMoney
=
new
Money
(
4002
,
"KRW"
)
expectedMoneyAfterDivision
=
new
Money
(
1000.5
,
"KRW"
)
assert
.
deepStrictEqual
(
originalMoney
.
divide
(
4
),
expectedMoneyAfterDivision
)
Here’s how the test_money.py
file looks right now:
import
unittest
class
Money
:
def
__init__
(
self
,
amount
,
currency
):
self
.
amount
=
amount
self
.
currency
=
currency
def
times
(
self
,
multiplier
):
return
Money
(
self
.
amount
*
multiplier
,
self
.
currency
)
def
divide
(
self
,
divisor
):
return
Money
(
self
.
amount
/
divisor
,
self
.
currency
)
def
__eq__
(
self
,
other
):
return
self
.
amount
==
other
.
amount
and
self
.
currency
==
other
.
currency
class
TestMoney
(
unittest
.
TestCase
):
def
testMultiplicationInDollars
(
self
):
fiveDollars
=
Money
(
5
,
"USD"
)
tenDollars
=
Money
(
10
,
"USD"
)
self
.
assertEqual
(
tenDollars
,
fiveDollars
.
times
(
2
))
def
testMultiplicationInEuros
(
self
):
tenEuros
=
Money
(
10
,
"EUR"
)
twentyEuros
=
Money
(
20
,
"EUR"
)
self
.
assertEqual
(
twentyEuros
,
tenEuros
.
times
(
2
))
def
testDivision
(
self
):
originalMoney
=
Money
(
4002
,
"KRW"
)
expectedMoneyAfterDivision
=
Money
(
1000.5
,
"KRW"
)
self
.
assertEqual
(
expectedMoneyAfterDivision
,
originalMoney
.
divide
(
4
))
if
__name__
==
'__main__'
:
unittest
.
main
()
1 ∀ x ≠ 0, i.e. as long as x isn’t zero … thank you, all the math teachers, for what you do!
2 The ===
operator tests whether both the values and the types of the two objects being compared are equal. See https://www.w3schools.com/nodejs/met_assert_deepstrictequal.asp
3 The ECMAScript standard defines a method as a “function that is the value of a property [of an object]” https://www.ecma-international.org/ecma-262/11.0/index.html#sec-method
4 The Python standard defines a method as “bound function objects”. That is, methods are always associated with objects, whereas functions are not. https://docs.python.org/3/c-api/method.html
18.220.137.164