Skip to content

Commit 6b0af0f

Browse files
committed
Added a test file for live testing our mini project using unittest library in order to practice wring testable code in a way that makes it easy to test individual components (e.g functions and classes) as well updated the README.md for latest documentation
1 parent 023fef4 commit 6b0af0f

2 files changed

Lines changed: 121 additions & 14 deletions

File tree

README.md

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,82 @@
11
# python-logic-lab
2-
Mini python solutions
32

4-
# 🐍 Python Logic Lab: Practical Scripts & Mini-Apps
3+
Mini python solutions
54

6-
A collection of lightweight Python projects demonstrating core programming logic,
7-
problem-solving, and automation. This repository showcases my ability to bridge
5+
# Python Logic Lab: Practical Scripts & Mini-Apps
6+
7+
A collection of lightweight Python projects demonstrating core programming logic,
8+
problem-solving, and automation. This repository showcases my ability to bridge
89
technical code with real-world scenarios—from ATM simulations to data processing tools.
910

10-
### 🚀 Featured Scripts
11-
* **ATM Simulation:** A robust terminal-based app with transaction logging and error handling.
12-
* **Network Port Scanner:** A multi-threaded utility that probes specific IP addresses for open ports. It demonstrates knowledge of the `socket` library, TCP handshakes, and network diagnostics—essential for technical SEO and web security audits.
13-
* **Data Utility Tools:** Scripts designed to clean and prep data for Excel & Power BI.
14-
* **AI & API Experiments:** Exploring rapid tech innovations through integrated Python libraries.
11+
### Featured Scripts
12+
13+
- **ATM Simulation:** A robust terminal-based app with transaction logging and error handling.
14+
15+
# ATM Mini Project
16+
17+
Short description
18+
19+
- Small CLI ATM simulator that supports withdrawals.
20+
- Core file: `atm_project.py`. Tests live in `test_atm.py`.
21+
- Designed for learning I/O, basic validation, logging and unit testing with mocks.
22+
23+
Files
24+
25+
- atm_project.py — main program and function `atm_withdrawal(balance)`:
26+
- Prompts user for an amount via input()
27+
- Validates numeric/positive/available funds
28+
- Prints result messages and returns the updated balance (float)
29+
- Logs transactions to `atm_transactions.log`
30+
- test_atm.py — unit tests that:
31+
- Mock `builtins.input` to simulate user input
32+
- Capture `sys.stdout` to assert printed messages
33+
- Assert returned balance values
34+
35+
Requirements
36+
37+
- Python 3.8+
38+
- No external packages required
39+
40+
How to run the program (Windows)
41+
42+
- From project folder:
43+
- python atm_project.py
44+
- Interact via the menu; choose "1" to withdraw and follow prompts.
45+
46+
How to run tests (Windows)
1547

16-
### 🛠️ Tech Stack
17-
* **Language:** Python 3.12+
18-
* **Core Libraries:** `datetime`, `logging`, `math`, `sys`, `socket`, `threading`
19-
* **Concepts:** Error Handling (Try/Except), File I/O, Data Validation, and Functional Programming.
48+
- From project folder:
49+
- python -m unittest c:\Users\sabia_kenya\Desktop\LearningPython_Basics\test_atm.py
50+
51+
Notes about tests
52+
53+
- Tests do not modify `atm_project.py`. They mock `input()` and capture stdout so the I/O code can be tested without changing the source.
54+
- Tests assert:
55+
- Correct updated balance returned by `atm_withdrawal`
56+
- Presence of expected printed messages (e.g., "Withdrawal successful", "Insufficient funds", "Invalid amount", and numeric-input error message)
57+
58+
Suggestions for future improvements
59+
60+
- Separate business logic from I/O: create functions like `process_withdrawal(balance, amount)` that only implement logic and return (new_balance, message). This simplifies testing (no mocking).
61+
- Add deposit functionality and persistence (file or DB) if desired.
62+
- Add a `reset_balance()` helper or class-based ATM to isolate state across tests.
63+
64+
Logging
65+
66+
- Transactions are appended to `atm_transactions.log` in the project directory with timestamps (configured in `atm_project.py`).
2067

2168
---
22-
*Building at the intersection of Data, Marketing, and Code.*
69+
70+
- **Network Port Scanner:** A multi-threaded utility that probes specific IP addresses for open ports. It demonstrates knowledge of the `socket` library, TCP handshakes, and network diagnostics—essential for technical SEO and web security audits.
71+
- **Data Utility Tools:** Scripts designed to clean and prep data for Excel & Power BI.
72+
- **AI & API Experiments:** Exploring rapid tech innovations through integrated Python libraries.
73+
74+
### Tech Stack
75+
76+
- **Language:** Python 3.12+
77+
- **Core Libraries:** `datetime`, `logging`, `math`, `sys`, `socket`, `threading`,`unittest`,`unittest.mock` ,`io`
78+
- **Concepts:** Error Handling (Try/Except), File I/O, Data Validation, and Functional Programming.
79+
80+
---
81+
82+
_Building at the intersection of Data, Marketing, and Code._

test_atm.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import unittest
2+
from atm_project import atm_withdrawal
3+
from unittest.mock import patch
4+
import io
5+
6+
7+
class TestATM(unittest.TestCase):
8+
9+
def test_withdrawal_success(self):
10+
initial = 500.0
11+
with patch("builtins.input", return_value="100"), patch(
12+
"sys.stdout", new=io.StringIO()
13+
) as fake_out:
14+
new_balance = atm_withdrawal(initial)
15+
self.assertAlmostEqual(new_balance, 400.0)
16+
self.assertIn("Withdrawal successful", fake_out.getvalue())
17+
18+
def test_withdrawal_insufficient(self):
19+
initial = 500.0
20+
with patch("builtins.input", return_value="2000"), patch(
21+
"sys.stdout", new=io.StringIO()
22+
) as fake_out:
23+
new_balance = atm_withdrawal(initial)
24+
self.assertEqual(new_balance, initial)
25+
self.assertIn("Insufficient funds", fake_out.getvalue())
26+
27+
def test_withdrawal_invalid_negative(self):
28+
initial = 500.0
29+
with patch("builtins.input", return_value="-50"), patch(
30+
"sys.stdout", new=io.StringIO()
31+
) as fake_out:
32+
new_balance = atm_withdrawal(initial)
33+
self.assertEqual(new_balance, initial)
34+
self.assertIn("Invalid amount", fake_out.getvalue())
35+
36+
def test_withdrawal_non_numeric(self):
37+
initial = 500.0
38+
with patch("builtins.input", return_value="abc"), patch(
39+
"sys.stdout", new=io.StringIO()
40+
) as fake_out:
41+
new_balance = atm_withdrawal(initial)
42+
self.assertEqual(new_balance, initial)
43+
self.assertIn("Please enter a valid numerical amount", fake_out.getvalue())
44+
45+
46+
if __name__ == "__main__":
47+
unittest.main()

0 commit comments

Comments
 (0)