Skip to content

Commit a58cda6

Browse files
committed
Update reqs, provide starter tests
1 parent af54367 commit a58cda6

File tree

7 files changed

+122
-77
lines changed

7 files changed

+122
-77
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[pylintrc]
22
# R0903: too few many public methods
3-
disable=R0903
3+
disable=R0903,missing-docstring

README.md

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Vending machine – Take 2: Using OOP
22

3-
This tutorial revists a previous example and uses object-oriented programming
3+
This tutorial revisits a previous example and uses object-oriented programming
44
paradigms to represent the concepts related to a vending machine. We will see
55
how classes and objects will help us better represent the "real world" and help
66
us track state:
@@ -11,21 +11,18 @@ Let's begin by designing our vending machine by modelling different objects.
1111
First, we have:
1212
- Coins
1313
- Products
14-
- DollarAmount
14+
- Vending Machine
1515

1616
We can consider these as abstract classes / concepts. In practise, we have
1717
specific coins like quarters, loonies & toonies. For products, we have chips,
1818
candy and drinks – these could be broken down further into specific products
19-
like "355ml Coca-Cola can". These are considered concrete classes which all share
20-
common properties to the abstracts coins and products.
21-
22-
Money in this case is a subclass of the python Decimal class and is used to
23-
represent a dollar amount. It is already provided as part of the boilerplate.
19+
like "355ml Coca-Cola can". These are considered concrete classes which all
20+
share common properties to the abstracts coins and products.
2421

2522
We must also model our vending machine and define actions on it:
2623
- `+ insert_coin(coin: Coin)`
2724
- `+ buy_product(product: str) -> Product`
28-
- `+ get_balance() -> Money`
25+
- `+ get_balance() -> int`
2926
- `+ get_change() -> List[Coin]`
3027

3128

@@ -35,38 +32,42 @@ We must also model our vending machine and define actions on it:
3532

3633
| **Coin** |
3734
| - |
38-
| `value: DollarAmount` |
35+
| `value: int` |
3936
| `label: str` |
4037
| `str() -> str` |
4138

4239

43-
| **FiveCent(Coin)** | |
40+
The following classes inherit from the parent `Coin` class. Inheritance is
41+
achieved by providing parentheses around the class definition and providing the
42+
parent class(es) as arguments.
43+
44+
| **Nickel(Coin)** | |
4445
| - | - |
45-
| value | `DollarAmount('0.05')` |
46+
| value | `5` |
4647
| `str()` | `'5¢'`|
4748

4849

49-
| **TenCent(Coin)** | |
50+
| **Dime(Coin)** | |
5051
| - | - |
51-
| value | `DollarAmount('0.10')` |
52+
| value | `10` |
5253
| `str()` | `'10¢'` |
5354

5455

5556
| **Quarter(Coin)** | |
5657
| - | - |
57-
| value | `DollarAmount('0.25')`|
58+
| value | `25`|
5859
| `str()` | `'25¢'` |
5960

6061

6162
| **Loonie(Coin)** | |
6263
| - | - |
63-
| value | `DollarAmount('1.00')`|
64+
| value | `100`|
6465
| `str()` | `'$1'` |
6566

6667

6768
| **Toonie(Coin)** | |
6869
| - | - |
69-
| value | `DollarAmount('2.00')`|
70+
| value | `200`|
7071
| `str()` | `'$2'` |
7172

7273

@@ -75,34 +76,41 @@ We must also model our vending machine and define actions on it:
7576
| **Product** |
7677
| --- |
7778
| `name: str` |
78-
| `price: DollarAmount` |
79+
| `price: int` |
80+
| `str() -> str` |
7981

8082

8183
| **Chips(Product)** | |
8284
| - | - |
8385
| `name` | `'Chips'` |
84-
| `price` | `DollarAmount('2.25')`|
86+
| `price` | `225` |
87+
| `str() -> str` | `Chips: $2.25` |
8588

8689

8790
| **Drink(Product)** | |
8891
| - | - |
8992
| `name` | `'Drink'` |
90-
| `price` | `DollarAmount('2.75')`|
93+
| `price` | `275` |
94+
| `str() -> str` | `Drink: $2.75` |
9195

9296

9397
| **Candy(Product)** | |
9498
| - | - |
9599
| `name` | `'Candy'` |
100+
| `price` | `315` |
101+
| `str()` | `Candy: $3.15` |
96102

97103

98104

99105
#### Vending Machine
100-
| **VendingMachine** |
101-
| - |
102-
| - `+ insert_coin(coin: Coin)` |
103-
| - `+ buy_product(product: str) -> Product`|
104-
| - `+ get_balance() -> Money` |
105-
| - `+ get_change() -> List[Coin]` |
106+
| **VendingMachine** |
107+
| -
108+
| `coins: List[Coin]` |
109+
| `purchases: List[Product]` |
110+
| `insert_coin(coin: Coin)` |
111+
| `buy_product(product: str) -> Product` |
112+
| `get_balance() -> int` |
113+
| `get_change() -> List[Coin]` |
106114

107115

108116

@@ -125,7 +133,7 @@ We must also model our vending machine and define actions on it:
125133
the purchase should be added to a list of purchases on the object.
126134

127135

128-
### .get_balance() -> Money
136+
### .get_balance() -> int
129137

130138
- The get_balance function should return the sum of inserted coins minus the
131139
sum of the price of purchased products.

models/__init__.py

Whitespace-only changes.

models/money.py

Lines changed: 0 additions & 17 deletions
This file was deleted.

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
pytest==4.3.0
2-
pylint==2.3.1
1+
pytest
2+
pylint

tests/test_coins.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from dataclasses import FrozenInstanceError
2+
import pytest
3+
4+
import coins
5+
6+
7+
def test_nickel_value():
8+
coin = coins.Nickel()
9+
assert coin.value == 5
10+
11+
12+
def test_nickel_str():
13+
coin = coins.Nickel()
14+
assert str(coin) == '5¢'
15+
16+
17+
def test_dime_value():
18+
coin = coins.Dime()
19+
assert coin.value == 10
20+
21+
22+
def test_dime_str():
23+
coin = coins.Dime()
24+
assert str(coin) == '10¢'
25+
26+
27+
def test_quarter_value():
28+
coin = coins.Quarter()
29+
assert coin.value == 25
30+
31+
32+
def test_quarter_str():
33+
coin = coins.Quarter()
34+
assert str(coin) == '25¢'
35+
36+
37+
def test_loonie_value():
38+
coin = coins.Loonie()
39+
assert coin.value == 100
40+
41+
42+
def test_loonie_str():
43+
coin = coins.Loonie()
44+
assert str(coin) == '$1'
45+
46+
47+
def test_toonie_value():
48+
coin = coins.Toonie()
49+
assert coin.value == 200
50+
51+
52+
def test_toonie_str():
53+
coin = coins.Toonie()
54+
assert str(coin) == '$2'
55+
56+
57+
def test_the_coin_constructor_takes_no_arguments():
58+
"""
59+
Ensure that a developer does not accidentally overwrite the
60+
coin `value` by providing an argument.
61+
62+
In other words, we must ensure that __init__ accepts no
63+
arguments.
64+
"""
65+
with pytest.raises(TypeError):
66+
coins.Coin(100) # Ensure that this isn't possible
67+
68+
69+
def test_the_instance_value_property_is_immutable():
70+
"""
71+
Ensure that the `value` property of the object cannot be
72+
modified on an instance of a coin class.
73+
74+
This is a special feature of Python 3.7 dataclasses which
75+
allow instance immutability by setting `frozen=True` in the
76+
dataclass decorator.
77+
78+
E.g.
79+
>>> @dataclass(frozen=True)
80+
>>> class MyClass:
81+
>>> ...
82+
"""
83+
coin = coins.Quarter()
84+
with pytest.raises(FrozenInstanceError):
85+
coin.value = 100 # Ensure that this isn't possible

tests/test_money.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)