Ачааллж байна...
Түвшин 3-ын төгсгөлд ирлээ. Бүлэг 7-9-д бид pytest-ийг дадлагын фолдерт суулгаж, тест бичиж, уналтыг уншиж, tests фолдер ба pytest.ini-ээр зохион байгуулж, fixture, parametrize, raises, tmp_path-ийг эзэмшиж, тестлэхэд эвтэй кодын санааг ойлгосон. Одоо бүгдийг нь жинхэнэ төсөл дээр нэгтгэнэ. Энэ удирдлагатай бүтээлд зарлагын бүртгэлд бүрэн тестийн цуглуулга бичнэ: тооцооллын функцууд, мөнгөний хэлбэржүүлэлт, файлд хадгалах ба ачаалах. Мөн хоёр шинэ функцийг, ангиллаар шүүх болон оролтоо шалгадаг зарлага үүсгэх функцийг, анх удаа тестийг нь эхэлж бичих аргаар бүтээнэ.
Хамгийн чухал нь энэ бүх ажлыг Түвшин 1, 2-ын дадлаар хийнэ: issue нээх, салаа үүсгэх, жижиг утга бүхий commit-ууд, pull request, өөрийгөө хянах, merge. Гурван Түвшингийн дадал нэг ажилд нийлэх нь энэ сургалтын хамгийн чухал мөчүүдийн нэг юм. Ажлын хугацаа 60-90 минут орчим. Алхам бүрийн дараа шалгах цэг бий. Хичээлийн төгсгөлд бүх алхмын дараалал байгаа.
Таны зарлагын бүртгэл энэ сургалтын жишээнээс бага зэрэг өөр байж болно: файлын нэр, функцийн нэр, dictionary-ийн түлхүүрийн нэр. Тийм бол жишээн дэх нэрсийг өөрийнхөөрөө солиорой. Тестүүдийн бүтэц ижил хэвээр байна.
Алхам 1: Хамгийн сүүлийн байдал
Гэрийн компьютерийн expense-tracker фолдероо VS Code-д нээнэ үү.
Command:
git switch main
git pull
git statusГаралт (сүүлийн мөрүүд):
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree cleanШалгах цэг: main дээр, GitHub-тай ижил, ажлын фолдер цэвэр.
Алхам 2: Ажлаа issue болгох
Browser дээр repository-гийнхоо Issues табыг нээж, “New issue” дарна уу. Гарчиг:
Add automated tests with pytestТайлбар:
markdown
Add a pytest test suite for the project.
- [ ] Set up pytest (dev requirements, pytest.ini, .gitignore)
- [ ] Tests for report functions
- [ ] Tests for formatting
- [ ] Tests for saving and loading with tmp_path
- [ ] New `filter_by_category()` function, written test-first
- [ ] New `make_expense()` with input validation, written test-first
- [ ] README section on running tests“- [ ]” гэсэн хэлбэр Markdown-ийн шинэ хэсэг: GitHub түүнийг даран сонгож болох хайрцагтай жагсаалт болгон харуулдаг. Ажлын явцад хайрцгуудыг issue хуудсан дээр даран тэмдэглэж болно. Энэ бол том ажлыг жижиг алхмуудад хувааж, явцыг харуулах энгийн арга. “enhancement” шошго нэмээд issue үүсгэнэ үү. Энэ issue #5 дугаартай болно. Хэрвээ та өмнөх хичээлүүдийн нэмэлт боломжуудыг хийсэн бол таны дугаар өөр байх бөгөөд энэ хичээлийн “#5”-ыг өөрийнхөөрөө солиорой.
[Зураг: хайрцагтай жагсаалт бүхий issue]
Шалгах цэг: Тестийн ажлын төлөвлөгөө дугаартай issue болон бүртгэгдсэн.
Алхам 3: Салаа
Command:
git switch -c add-testsГаралт:
Switched to a new branch 'add-tests'Шалгах цэг: VS Code-ийн зүүн доод буланд “add-tests”.
Алхам 4: pytest суулгах
Venv-ээ идэвхжүүлж, pytest суулгана уу. 7.2 дугаар хичээлийн сануулгыг санаарай: мөрийн эхэнд “(venv)” байх ёстой.
Command:
.\venv\Scripts\Activate.ps1
pip install pytestГаралт (сүүлийн мөр):
Successfully installed colorama-0.4.6 iniconfig-2.1.0 packaging-25.0 pluggy-1.6.0 pygments-2.19.2 pytest-9.1.1Алхам 5: Хөгжүүлэлтийн хамаарал
5.2 дугаар хичээлд requirements.txt-ийг “# No external packages yet” гэсэн мөртэй үүсгэсэн. Одоо pytest нэмэгдсэн. Гэхдээ нэг нарийн асуулт бий: pytest бол программыг ажиллуулахад хэрэгтэй юу? Үгүй. Зарлагын бүртгэлийг зүгээр ашиглах хүнд pytest хэрэггүй, зөвхөн кодыг хөгжүүлж, тестлэх хүнд л хэрэгтэй. Ийм package-уудыг хөгжүүлэлтийн хамаарал буюу development dependency гэдэг бөгөөд тэдгээрийг тусдаа файлд бичих нь түгээмэл заншил.
Төслийн үндсэнд requirements-dev.txt нэртэй шинэ файл үүсгэж, нэг мөр бичнэ үү.
pytestrequirements.txt-ийг хөндөхгүй үлдээнэ: программ өөрөө гадны package шаардахгүй хэвээр. Хөгжүүлэгч pip install -r requirements-dev.txt бичиж тестийн хэрэгслүүдийг суулгана. Үүнийг README-д Алхам 20-д бичнэ.
Алхам 6: pytest.ini ба tests фолдер
8.1 дүгээр хичээлийн тохиргоог энд яг хэвээр нь ашиглана. Төслийн үндсэнд pytest.ini үүсгэнэ үү.
ini
[pytest]
pythonpath = .
testpaths = testsМөн tests нэртэй хоосон фолдер үүсгэнэ үү.
Алхам 7: .gitignore
7.2 дугаар хичээлд pytest .pytest_cache фолдер үүсгэдэг бөгөөд тэр цомогт орох ёсгүй гэж ярьсан. .gitignore-ийн “Python cache” хэсэгт мөр нэмнэ үү.
# Python cache
__pycache__/
*.pyc
.pytest_cache/Алхам 8: Тохиргоог шалгаж, commit хийх
Command:
pytestГаралт:
============================= test session starts =============================
platform win32 -- Python 3.13.7, pytest-9.1.1, pluggy-1.6.0
rootdir: C:\Users\Bat\expense-tracker
configfile: pytest.ini
testpaths: tests
collected 0 items
============================ no tests ran in 0.01s ============================“configfile: pytest.ini” ба “testpaths: tests” мөрүүд тохиргоо уншигдсаныг баталж байна. “no tests ran” нь одоохондоо хэвийн: tests фолдер хоосон.
Command:
git statusГаралт:
On branch add-tests
Untracked files:
(use "git add <file>..." to include in what will be committed)
pytest.ini
requirements-dev.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .gitignoreХоёр зүйлийг анзаараарай. .pytest_cache жагсаалтад байхгүй: .gitignore ажиллаж байна. Мөн tests фолдер жагсаалтад байхгүй: 2.1 дүгээр хичээлийн “Git зөвхөн файлуудыг хянадаг, хоосон фолдерыг харахгүй” гэсэн дүрэм. Дараагийн алхамд тэнд файл үүсэхэд л Git түүнийг харна. Статусын хэсгүүдийн дараалал таны Git-ийн хувилбараас хамаараад өөр байж болно.
Command:
git add .gitignore pytest.ini requirements-dev.txt
git commit -m "Set up pytest"Гаралт:
[add-tests 5c2e8a1] Set up pytest
3 files changed, 5 insertions(+)
create mode 100644 pytest.ini
create mode 100644 requirements-dev.txtШалгах цэг: “configfile: pytest.ini” харагдаж, тохиргооны commit хийгдсэн.
Алхам 9: Жишээ өгөгдлийн fixture
8.2 дугаар хичээлээс санаж байгаачлан олон тестийн файл хуваалцах fixture conftest.py-д байрлана. tests фолдерт conftest.py үүсгэнэ үү.
python
import pytest
@pytest.fixture
def sample_expenses():
return [
{"amount": 5000, "category": "хоол"},
{"amount": 12000, "category": "унаа"},
{"amount": 3500, "category": "хоол"},
]Энэ бол бидний data файлтай ижил гурван зарлага. Хүлээгдэж буй утгуудыг одоо толгойдоо бодъё, бүх тестэд хэрэг болно. Нийт: 5000 + 12000 + 3500 = 20500. Тоо: 3. Дундаж: 20500 хуваах 3, ойролцоогоор 6833.333. Ангиллаар: хоол 8500, унаа 12000.
Алхам 10: report-ийн тестүүд
tests фолдерт test_report.py үүсгэнэ үү.
python
import pytest
from report import average, count, total, total_by_category
def test_total(sample_expenses):
assert total(sample_expenses) == 20500
def test_count(sample_expenses):
assert count(sample_expenses) == 3
def test_average(sample_expenses):
assert average(sample_expenses) == pytest.approx(6833.333333)
@pytest.mark.parametrize(
"func, expected",
[(total, 0), (count, 0), (average, 0)],
ids=["total", "count", "average"],
)
def test_empty_list(func, expected):
assert func([]) == expected
def test_total_by_category(sample_expenses):
assert total_by_category(sample_expenses) == {"хоол": 8500, "унаа": 12000}
def test_total_by_category_empty():
assert total_by_category([]) == {}Хэд хэдэн сонирхолтой зүйлийг анзаараарай. test_average 7.3 дугаар хичээлийн pytest.approx-ийг ашиглаж байна, учир нь дундаж нь бутархай тоо. test_empty_list бол 8.3 дугаар хичээлийн parametrize, гэхдээ хүснэгтийн нэг багана нь тоо биш, функц өөрөө: Python-д функц бол утга тул түүнийг жагсаалтад хийж, параметрээр дамжуулж болно. Нэг тест гурван функцийн хоосон жагсаалтын зан төлөвийг шалгадаг. Ялангуяа average-ийн мөр онцгой: энэ бол 6.1 дүгээр хичээлд issue #1-ээр зассан алдааны харуул. Хэрвээ хэн нэгэн “if not expenses” мөрийг хэзээ нэгэн цагт устгавал энэ тест шууд унана. 7.1 дүгээр хичээлд "тэр үед тест бичсэн бол regression-оос хамгаалах байсан" гэж ярьсан. Одоо бичлээ.
Command:
pytest -vГаралт:
collected 8 items
tests/test_report.py::test_total PASSED [ 12%]
tests/test_report.py::test_count PASSED [ 25%]
tests/test_report.py::test_average PASSED [ 37%]
tests/test_report.py::test_empty_list[total] PASSED [ 50%]
tests/test_report.py::test_empty_list[count] PASSED [ 62%]
tests/test_report.py::test_empty_list[average] PASSED [ 75%]
tests/test_report.py::test_total_by_category PASSED [ 87%]
tests/test_report.py::test_total_by_category_empty PASSED [100%]
============================== 8 passed in 0.03s ==============================Найман тест, бүгд ногоон. Хэрвээ аль нэг нь унавал 7.4 дүгээр хичээлийн аргаар уншаад "код уу, тест үү" гэж асуугаарай. Таны функцууд жишээнийхээс өөр нэртэй эсвэл өөр түлхүүр ашигладаг бол уналт ихэвчлэн нэрийн зөрүүнээс үүснэ.
Command:
git add tests
git commit -m "Add tests for report functions"Шалгах цэг: report-ийн 8 тест давсан, commit хийгдсэн.
Алхам 11: formatting-ийн тестүүд
tests фолдерт test_formatting.py үүсгэнэ үү.
python
import pytest
from formatting import format_amount
@pytest.mark.parametrize(
"amount, expected",
[
(0, "0₮"),
(500, "500₮"),
(1000, "1,000₮"),
(20500, "20,500₮"),
(1234567, "1,234,567₮"),
],
ids=["zero", "below-thousand", "thousand", "typical", "millions"],
)
def test_format_amount(amount, expected):
assert format_amount(amount) == expected8.3 дугаар хичээлийн хүснэгтийн сэтгэлгээ: таслалгүй жижиг тоо, таслал эхлэх яг 1000-ийн хил, ердийн дүн, хоёр таслалтай сая.
Command:
pytest
git add tests\test_formatting.py
git commit -m "Add tests for formatting"Гаралт (pytest-ийн сүүлийн мөр):
============================= 13 passed in 0.03s ==============================Шалгах цэг: 13 тест давсан, commit хийгдсэн.
Алхам 12: storage-ийн тестүүд
9.1 дүгээр хичээлийн дөрвөн тестийн загварыг зарлагын бүртгэлийн storage-д хэрэглэе. tests фолдерт test_storage.py үүсгэнэ үү.
python
import pytest
from storage import load_expenses, save_expenses
def test_save_and_load_round_trip(tmp_path, sample_expenses):
path = tmp_path / "expenses.json"
save_expenses(path, sample_expenses)
assert load_expenses(path) == sample_expenses
def test_load_reads_existing_file(tmp_path):
path = tmp_path / "expenses.json"
path.write_text('[{"amount": 5000, "category": "хоол"}]', encoding="utf-8")
assert load_expenses(path) == [{"amount": 5000, "category": "хоол"}]
def test_saved_file_keeps_cyrillic(tmp_path, sample_expenses):
path = tmp_path / "expenses.json"
save_expenses(path, sample_expenses)
assert "хоол" in path.read_text(encoding="utf-8")
def test_load_missing_file_raises(tmp_path):
with pytest.raises(FileNotFoundError):
load_expenses(tmp_path / "missing.json")storage.py-ийн функцуудын параметрийн дараалал жишээнийхтэй ижил эсэхийг шалгаарай: 4.4 дүгээр хичээлд бид save_expenses(path, expenses) гэж бичсэн.
Command:
pytestГаралт (сүүлийн мөр):
============================= 17 passed in 0.04s ==============================Энэ удаад round-trip тест анхнаасаа давсан. 9.1 дүгээр хичээлд tuple-ийг list болгодог асуудал гарсныг санаарай. Зарлага бүр dictionary бөгөөд JSON-д dictionary-ийн яг тохирох төрөл байдаг тул төрөл өөрчлөгдөхгүй. Тест давсан нь бидэнд мэдээлэл өгч байна: энэ төсөлд тэр асуудал байхгүй.
Одоо 9.1 дүгээр хичээлийн нэг чухал шалгалтыг хийе.
Command:
git statusГаралт:
On branch add-tests
Untracked files:
(use "git add <file>..." to include in what will be committed)
tests/test_storage.py
nothing added to commit but untracked files present (use "git add" to track)Зөвхөн шинэ тестийн файл. Тестүүд дөрвөн JSON файл бичиж, уншсан ч нэг нь ч таны төсөлд үлдээгүй: бүгд tmp_path-д байсан. Мөн таны жинхэнэ data/expenses.json хөндөгдөөгүй.
Command:
git add tests\test_storage.py
git commit -m "Add storage round-trip tests"Шалгах цэг: 17 тест давсан, git status тест ажиллуулсны дараа хог харуулаагүй, commit хийгдсэн.
Одоог хүртэл бид байгаа кодонд тест нэмсэн. Одоо дарааллыг эргүүлье: шинэ функцийн тестийг функцээс өмнө бичнэ. Энэ аргыг test-driven development буюу тестээр удирдсан хөгжүүлэлт гэдэг бөгөөд түүний гол санаа энгийн: тест бичих нь функц юу хийх ёстойг, гэрээг нь, кодыг бичихээс өмнө тодорхой болгуулдаг. Энэ сургалт энэ аргыг заавал баримтлахыг шаардахгүй, гэхдээ нэг удаа туршиж үзэх нь гэрээг урьдчилан бодох дадлыг төлөвшүүлнэ.
Алхам 13: Шүүлтүүрийн тест, функцгүйгээр
Ангиллаар шүүх функц хэрэгтэй гэж бодъё. Эхлээд юу хийх ёстойг тестээр бичье. tests\test_report.py-ийн import мөрөнд filter_by_category нэмж, төгсгөлд бичнэ үү.
python
from report import average, count, filter_by_category, total, total_by_categorypython
def test_filter_by_category(sample_expenses):
assert filter_by_category(sample_expenses, "хоол") == [
{"amount": 5000, "category": "хоол"},
{"amount": 3500, "category": "хоол"},
]
def test_filter_by_category_no_match(sample_expenses):
assert filter_by_category(sample_expenses, "эм") == []Хоёр тест гэрээг тодорхойлж байна: тухайн ангиллын зарлагуудыг анхны дарааллаар нь буцаана, тохирох зарлага байхгүй бол хоосон жагсаалт.
Command:
pytestГаралт (гол хэсэг):
collected 9 items / 1 error
=================================== ERRORS ====================================
____________________ ERROR collecting tests/test_report.py ____________________
ImportError while importing test module 'C:\Users\Bat\expense-tracker\tests\test_report.py'.
...
E ImportError: cannot import name 'filter_by_category' from 'report' (C:\Users\Bat\expense-tracker\report.py)
=========================== short test summary info ===========================
ERROR tests/test_report.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!8.1 дүгээр хичээлийн цуглуулах үеийн ERROR. “cannot import name ‘filter_by_category’ from ‘report’” нь “report-оос filter_by_category нэрийг import хийж чадсангүй” гэсэн утгатай. Энэ бол яг бидний хүлээсэн зүйл: функц хараахан байхгүй. Тестээр удирдсан хөгжүүлэлтэд энэ улаан мөч хэвийн бөгөөд хэрэгтэй: тест функцийн байхгүйг баталсан, тиймээс функц бичигдмэгц тест үнэхээр түүнийг шалгана гэдэгт итгэлтэй болно.
Алхам 14: Функцийг бичих
report.py-ийн төгсгөлд нэмнэ үү.
python
def filter_by_category(expenses, category):
return [e for e in expenses if e["category"] == category]Command:
pytestГаралт (сүүлийн мөр):
============================= 19 passed in 0.04s ==============================Улаанаас ногоон. Функц цэвэр, 9.2 дугаар хичээлийн утгаар: жагсаалт аваад шинэ жагсаалт буцаана, юуг ч хэвлэдэггүй, өгсөн жагсаалтыг өөрчилдөггүй.
Command:
git add report.py tests\test_report.py
git commit -m "Add filter_by_category function"Шалгах цэг: Тест эхлээд ERROR-оор унаж, функц бичигдсэний дараа 19 тест давсан, commit хийгдсэн.
Алхам 15: Оролтоо шалгадаг функцийн тестүүд
Одоо программд шинэ зарлага нэмэх үед ашиглах функц хэрэгтэй. Тэр функц 8.4 дүгээр хичээлийн apply_discount шиг буруу оролтыг эсэргүүцэх ёстой: дүн эерэг байх ёстой, ангилал хоосон байж болохгүй. Энэ нь тооцоолол ч биш, хадгалалт ч биш тул шинэ модуль expenses.py-д байрлана. 8.1 дүгээр хичээлийн дүрмээр түүний тест test_expenses.py-д. Эхлээд тестүүдийг бичнэ. tests фолдерт test_expenses.py үүсгэнэ үү.
python
import pytest
from expenses import make_expense
def test_make_expense():
assert make_expense(5000, "хоол") == {"amount": 5000, "category": "хоол"}
@pytest.mark.parametrize("amount", [0, -100])
def test_make_expense_rejects_non_positive_amount(amount):
with pytest.raises(ValueError, match="positive"):
make_expense(amount, "хоол")
def test_make_expense_rejects_empty_category():
with pytest.raises(ValueError, match="category"):
make_expense(5000, "")Гэрээ гурван хэсэгтэй: зөв оролтоос зарлагын dictionary үүсгэнэ, тэг болон сөрөг дүнг “positive” гэсэн үгтэй мессежээр эсэргүүцнэ, хоосон ангиллыг “category” гэсэн үгтэй мессежээр эсэргүүцнэ. Тэг хүснэгтэд байгаа нь 8.4 дүгээр хичээлийн хил дээрх сэтгэлгээ: тэг бол эерэг биш, зөвшөөрөгдөхгүй хамгийн ойрын утга.
Command:
pytestГаралт (гол хэсэг):
E ModuleNotFoundError: No module named 'expenses'
=========================== short test summary info ===========================
ERROR tests/test_expenses.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!Энэ удаад модуль өөрөө байхгүй: “expenses нэртэй модуль олдсонгүй”. Хүлээгдэж буй улаан.
Алхам 16: Функцийг бичих
Төслийн үндсэнд expenses.py үүсгэнэ үү.
python
def make_expense(amount, category):
if amount <= 0:
raise ValueError(f"Amount must be positive, got {amount}")
if not category:
raise ValueError("Expense category must not be empty")
return {"amount": amount, "category": category}Command:
pytestГаралт:
collected 23 items
tests\test_expenses.py .... [ 17%]
tests\test_formatting.py ..... [ 39%]
tests\test_report.py .......... [ 82%]
tests\test_storage.py .... [100%]
============================= 23 passed in 0.05s ==============================Дөрвөн тестийн файл, дөрвөн модуль, хорин гурван тест, бүгд ногоон. Хэрвээ хүсвэл 8.4 дүгээр хичээлийн аргаар дохиоллыг шалгаж болно: make_expense-ийн эхний шалгалтыг “#”-ээр түр унтраахад хүснэгтийн хоёр тест “DID NOT RAISE”-ээр унах ёстой. Дараа нь буцааж идэвхжүүлнэ үү.
Command:
git add expenses.py tests\test_expenses.py
git commit -m "Add make_expense with input validation"Шалгах цэг: 23 тест давсан, commit хийгдсэн.
Алхам 17: README
Тестүүд repository-д байгаа ч тэдгээрийг хэрхэн ажиллуулахыг хэн ч мэдэхгүй бол хагас үнэ цэнтэй. 6.3 дугаар хичээлийн сургамжаар README-г урьдчилан шинэчилье. README.md-ийн “How to run” хэсгийн дараа шинэ хэсэг нэмнэ үү.
markdown
## Running tests
Install the test tools and run the tests:
```powershell
pip install -r requirements-dev.txt
pytest
```Мөн “Project structure” хэсгийг шинэчилнэ үү.
markdown
- `expenses.py` — creating expenses with input checks
- `tests/` — pytest test suiteCommand:
git add README.md
git commit -m "Document how to run tests"Алхам 18: Салааны түүх
Command:
git log --oneline main..add-testsГаралт:
d81f4c7 (HEAD -> add-tests) Document how to run tests
a3e9b05 Add make_expense with input validation
7f1c6d2 Add filter_by_category function
e4b8a19 Add storage round-trip tests
9c0d3f6 Add tests for formatting
2b7e5a8 Add tests for report functions
5c2e8a1 Set up pytestДолоон commit, тус бүр нэг алхам, issue-ийн хайрцагтай жагсаалттай бараг яг таарч байна. Reviewer энэ түүхийг уншаад ажил хэрхэн хийгдсэнийг алхам алхмаар харна. Таны hash-ууд өөр байна.
Шалгах цэг: Салаа долоон тодорхой commit-тэй.
Алхам 19: Push ба pull request
Command:
git push -u origin add-testsGitHub дээр “Compare & pull request” дарж, “base: main ← compare: add-tests” чиглэлийг шалгаарай. Гарчиг: “Add automated tests with pytest”. Тайлбар:
markdown
Adds a pytest test suite and two new functions written test-first.
- pytest setup: `requirements-dev.txt`, `pytest.ini`, `.pytest_cache/` ignored
- Tests for report, formatting and storage (storage tests use `tmp_path`)
- New `filter_by_category()` and `make_expense()` with input validation
- README explains how to run the tests
23 tests, all passing locally.
Closes #5“Create pull request” дарна уу. Энэ pull request #6 дугаартай болно.
Шалгах цэг: Pull request нээлттэй, issue #5-тай холбогдсон.
Алхам 20: Өөрийгөө хянах
Files changed табыг нээж, 6.2 дугаар хичээлийн редакторын асуултуудаар уншаарай. Энэ удаад нэг нэмэлт асуулт бий: тест бүр функц буруу ажиллавал унах уу? 7.3 дугаар хичээлийн "хэт ерөнхий шалгалт"-ын анхааруулгыг санаарай. Хүлээгдэж буй утга бүр гараар бодогдсон уу, кодоос хуулагдаагүй юу? Хүснэгтүүдийн мөр бүр утга бүхий тохиолдол уу? Бүгд зөв бол Conversation табд “Reviewed: all 23 tests pass, expected values computed by hand.” гэх мэт сэтгэгдэл бичээрэй.
Шалгах цэг: Бүх өөрчлөлтийг тестийн чанарын өнцгөөс ч уншсан.
Алхам 21: Нэгтгэх ба шинэчлэх
“Merge pull request” дараад баталгаажуулж, “Delete branch” дарна уу. Issues табд issue #5 автоматаар хаагдсан байх ёстой.
Command:
git switch main
git pull
git branch -d add-tests
git fetch --prune
pytestГаралт (pytest-ийн хэсэг):
collected 23 items
tests\test_expenses.py .... [ 17%]
tests\test_formatting.py ..... [ 39%]
tests\test_report.py .......... [ 82%]
tests\test_storage.py .... [100%]
============================= 23 passed in 0.05s ==============================main дээр бүх тест ногоон. Таны зарлагын бүртгэл одоо хорин гурван харуултай. Цаашид кодонд хийх өөрчлөлт бүрийн дараа pytest бичихэд тэд бүгд хэдхэн секундэд шалгана.
Шалгах цэг: Pull request #6 нэгтгэгдсэн, issue #5 хаагдсан, main дээр 23 тест давсан.
# --- А. Төлөвлөгөө ба салаа ---
git switch main
git pull
git status
# GitHub: issue нээх, хайрцагтай жагсаалтаар төлөвлөх
git switch -c add-tests
# --- Б. pytest тохиргоо ---
.\venv\Scripts\Activate.ps1
pip install pytest
# requirements-dev.txt, pytest.ini, tests\, .gitignore-д .pytest_cache/
pytest
git add .gitignore pytest.ini requirements-dev.txt
git commit -m "Set up pytest"
# --- В. Байгаа кодын тестүүд (файл бүрийн дараа) ---
pytest
git add tests
git commit -m "Add tests for ..."
git status # тест хог үлдээгээгүйг шалгах
# --- Г. Тестээ эхэлж бичих (функц бүрт) ---
# 1. тест бичих
pytest # улаан: функц байхгүй
# 2. функц бичих
pytest # ногоон
git add ...
git commit -m "Add ... function"
# --- Д. Баримтжуулж, нэгтгэх ---
git commit -m "Document how to run tests"
git log --oneline main..add-tests
git push -u origin add-tests
# GitHub: pull request, "Closes #N", өөрийгөө хянах, merge, delete branch
git switch main
git pull
git branch -d add-tests
git fetch --prune
pytestЭнэ дараалал Түвшин 1-ийн commit, салаа, merge, Түвшин 2-ын issue, pull request, Түвшин 3-ын тест бүгдийг нэг ажилд нийлүүлж байна.
Доорх гурван санаа бүгд сонголт бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж үзэхгүй. Хүсвэл, сонирхвол туршаад үзээрэй.
main.py-ийг цэвэр болгох. 9.2 дугаар хичээлийн санааг зарлагын бүртгэлийн main.py-д хэрэглээрэй. Хураангуйн бүх мөрийг бүтээдэг build_summary(expenses) цэвэр функц бичиж, түүний тестийг эхлээд бичээрэй. Дараа нь main.py зөвхөн файл ачаалж, тэр текстийг хэвлэдэг нимгэн бүрхүүл болно. Энэ ажлыг шинэ issue, салаа, pull request-ээр хийгээрэй.
Хоёр дахь компьютерийн шалгалт. 4.3 дугаар хичээлийн expense-tracker-copy фолдерт git pull хийж, шинэ venv үүсгээд, README-ийн “Running tests” хэсгийг яг дагаж үзээрэй: pip install -r requirements-dev.txt, дараа нь pytest. Хэрвээ ямар нэг алхам дутуу байвал, жишээ нь data файл .gitignore-д байгаа учраас ямар нэг тест унавал, тэр нь README-ийн засах газар. Шинэ компьютер дээр тест ажиллаж байгааг батлах нь таны тестүүд жинхэнэ тусгаар гэдгийн хамгийн сайн нотолгоо.
Шинэ хувилбар. Хоёр шинэ функц нэмэгдсэн тул 6.3 дугаар хичээлийн аргаар v1.2.0 release гаргаж, “Generate release notes”-оор pull request #6-г тэмдэглэлд оруулж болно.
report модуль олдохгүй байх. Хэрвээ тест бүр “ModuleNotFoundError: No module named ‘report’”-оор цуглуулах үедээ унавал 8.1 дүгээр хичээлийн асуудал: pytest.ini төслийн үндсэнд байгаа эсэх, “[pytest]” гарчигтай эсэх, “pythonpath = .” мөр байгаа эсэхийг шалгаарай. Гаралтын толгойд “configfile: pytest.ini” мөр харагдах ёстой.
pytest танигдахгүй байх. Venv идэвхгүй эсвэл pytest энэ төслийн venv-д суугаагүй. Мөрийн эхний “(venv)”-ийг шалгаад, Алхам 4-ийг давтаарай. 7.2 дугаар хичээлийн “pip install-ийг venv-ээс гадуур хийх” анхааруулгыг санаарай: pytest-dadlaga-ийн venv-д суусан pytest энэ төсөлд хамаагүй.
KeyError-оор унах. Хэрвээ report-ийн тестүүд “KeyError: ‘category’” эсвэл “KeyError: ‘amount’”-оор унавал conftest.py-ийн sample_expenses-ийн түлхүүрүүд таны report.py-ийн хүлээж буй түлхүүрүүдээс өөр байна. Таны data файлд ямар түлхүүр ашиглагдаж байгааг шалгаад, fixture-ийг тааруулна уу. Fixture нэг газар тул нэг засвар бүх тестэд хүрнэ: 8.2 дугаар хичээлийн fixture-ийн давуу тал.
.pytest_cache git status-д харагдах. Хэрвээ Алхам 8-ын git status-д .pytest_cache/ харагдвал .gitignore-ийн мөр буруу бичигдсэн эсвэл хадгалагдаагүй байна. Мөрийг яг “.pytest_cache/” гэж, цэгтэй эхэлж, налуу зураасаар төгссөн эсэхийг шалгаарай. 2.4 дүгээр хичээлийн git check-ignore -v .pytest_cache нь аль дүрэм түүнийг нууж байгааг, эсвэл огт нуухгүй байгааг харуулна.
Тестээр удирдсан алхамд тест ERROR биш, давах. Хэрвээ Алхам 13 эсвэл 15-д функц бичихээсээ өмнө тест давсан бол ижил нэртэй функц аль хэдийн хаа нэгтээ байна, эсвэл тест буруу модулиас import хийж байна. Энэ бол тестээр удирдсан аргын яг давуу тал: "улаан" алхам тест таны бодсон зүйлийг шалгаж байгааг баталдаг. Хэрвээ улаан гарахгүй бол функцийг бичихээсээ өмнө шалтгааныг олоорой.
Энэ удирдлагатай бүтээлд та зарлагын бүртгэлд 23 тестийн бүрэн цуглуулга бичиж, хоёр шинэ функцийг тестээр удирдсан аргаар бүтээж, бүгдийг мэргэжлийн мөчлөгөөр нэгтгэсэн. Ажлыг хайрцагтай жагсаалт бүхий issue-ээр төлөвлөсөн. pytest-ийг хөгжүүлэлтийн хамаарал болгон requirements-dev.txt-д бичиж, pytest.ini болон .gitignore-оор тохируулсан. conftest.py-ийн sample_expenses fixture-ийг бүх тестийн файл хуваалцсан. report-ийн тестүүд pytest.approx-оор бутархай дунджийг, функцуудыг хүснэгтэд хийсэн parametrize-оор хоосон жагсаалтыг шалгаж, issue #1-ийн алдааг regression-оос хамгаалсан. storage-ийн тестүүд tmp_path-д ажиллаж, төсөлд хог үлдээгээгүй. Тестийг эхэлж бичихэд улаан ERROR функц байхгүйг баталж, функц бичигдмэгц ногоон болсон. make_expense буруу оролтыг pytest.raises ба match-ээр шалгагдсан ValueError-оор эсэргүүцдэг. README тестийг хэрхэн ажиллуулахыг тайлбарласан. Долоон жижиг commit, “Closes #5” бүхий pull request, өөрийгөө хянах, merge, local-ыг шинэчлэх, main дээр бүх тест ногоон.
Ингээд Түвшин 3 дууслаа. Таны код одоо хадгалагдсан, хуваалцагдсан, хамгаалагдсан. Гэхдээ тест "энд алдаа бий" гэж хэлэхэд түүнийг яг хаана, яагаад гэдгийг олох нь өөр ур чадвар. Түвшин 4-т бид кодын чанар ба дадлын тухай ярина. Бүлэг 10-ын эхний хичээлд debugging буюу алдаа хайх аргачлалыг сурна: таамаглал дэвшүүлэх, шалгах, хүрээг нарийсгах, мөн хүн бүрийн эхэлдэг print()-ийн арга яагаад сайн, хэзээ хүрэлцэхгүй болдог тухай шударгаар ярина.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.