Ачааллж байна...
Өмнөх хичээлд олон тест ижил бэлтгэлээр эхлэх давхардлыг fixture-ээр шийдсэн. Одоо өөр төрлийн давхардлыг харцгаая. test_prices.py-ийг нээгээд add_vat-ийн гурван тестийг хараарай: test_add_vat, test_add_vat_zero, test_add_vat_rounds_down. Гурвуулаа яг ижил бүтэцтэй: add_vat-ийг нэг тоогоор дуудаж, үр дүнг өөр нэг тоотой харьцуулна. Тэдгээрийн ялгаа нь зөвхөн хоёр тоо. Хэрвээ add_vat-ийг арван өөр үнээр шалгахыг хүсвэл арван бараг ижил функц бичих үү?
Энэ хичээлд pytest-ийн parametrize боломжийг сурна. Тэр нь тестийн бүтцийг нэг удаа бичиж, оролт ба хүлээгдэж буй үр дүнгийн хүснэгтийг өгөхөд хүснэгтийн мөр бүрийг тусдаа тест болгон ажиллуулдаг. Бид add_vat-ийн гурван тестийг нэг хүснэгтэд нэгтгэж, илүү олон хил дээрх тохиолдол нэмнэ. Хүснэгтийн аль мөр унасныг тайлангаас хэрхэн уншихыг харж, мөрүүдэд ойлгомжтой нэр өгч, жагсаалт зэрэг нийлмэл утгуудыг хүснэгтэд хэрхэн ашиглахыг сурна. Эцэст нь parametrize хэзээ тохиромжгүй болох тухай шударга ярина. Бид pytest-dadlaga фолдер дээр үргэлжлүүлнэ.
Нягтлан бодогч ажлын ширээн дээрээ татварын хүснэгт тавьдаг. Хүснэгтийн зүүн баганад орлогын дүн, баруун баганад харгалзах татвар. Мянган мөр байж болох ч хүснэгт бүхэлдээ нэг дүрмийг дагадаг: "энэ дүнд энэ татвар". Хэрвээ нягтлан бодогч программын тооцоог шалгахыг хүсвэл мөр бүрийн хувьд нэг ижил үйлдлийг хийнэ: программд зүүн баганын тоог өгч, гарсан үр дүнг баруун баганын тоотой харьцуулна. Шалгах арга нэг, тоо нь л мөр бүрт өөр.
parametrize бол яг ийм хүснэгтийн сэтгэлгээ. Тест функц нь шалгах арга: “add_vat-д дүнг өгөөд, үр дүнг хүлээгдэж буй утгатай харьцуул”. Хүснэгт нь тохиолдлууд: мөр бүрт нэг дүн, нэг хүлээгдэж буй үр дүн. Тест бичих үед та тохиолдол бүрт функц бичихээ больж, харин "ямар мөрүүд энэ хүснэгтэд байх ёстой вэ" гэж бодож эхэлнэ. Энэ нь хил дээрх тохиолдлуудын тухай бодоход илүү хялбар болгодог: хүснэгтийг хараад хоосон зай, дутуу мөр шууд харагддаг.
Алхам 1: Хүснэгтээ бодох
Кодыг бичихээсээ өмнө хүснэгтээ толгойдоо бодъё. add_vat-ийн хувьд ямар тохиолдлууд чухал вэ? Тэг: 0 → 0. Хамгийн бага эерэг тоо: 1, түүний арван хувь 0.1, доош тайрвал 0, тиймээс 1 → 1. Арван хувь яг бүхэл гарах хамгийн бага тоо: 10 → 11. Тайралт ажиллах тохиолдол: 99, арван хувь нь 9.9, доош нь 9, тиймээс 99 → 108. Ердийн тохиолдол: 1000 → 1100. Мянгын хил давсан тайралт: 1999, арван хувь нь 199.9, доош нь 199, тиймээс 1999 → 2198. Том ердийн тохиолдол: 5000 → 5500. Долоон мөр, тус бүр функцийн нэг шинжийг илэрхийлдэг.
Алхам 2: Хүснэгтийг кодонд бичих
VS Code-д pytest-dadlaga фолдероо нээж, venv идэвхтэй эсэхийг шалгаад tests\test_prices.py-г нээнэ үү. Эхлээд add_vat-ийн гурван хуучин тестийг, test_add_vat, test_add_vat_zero, test_add_vat_rounds_down-ийг устгана уу. Оронд нь дараахыг бичнэ үү. Файлын дээд хэсэгт 7.3 дугаар хичээлээс хойш “import pytest” байгаа эсэхийг шалгаарай.
python
@pytest.mark.parametrize(
"amount, expected",
[
(0, 0),
(1, 1),
(10, 11),
(99, 108),
(1000, 1100),
(1999, 2198),
(5000, 5500),
],
)
def test_add_vat(amount, expected):
assert add_vat(amount) == expectedЭнэ кодыг хэсэг хэсгээр уншъя. “@pytest.mark.parametrize” нь өмнөх хичээлийн @pytest.fixture-тэй адил decorator, гэхдээ өөр үүрэгтэй: "энэ тестийг олон утгаар ажиллуул" гэж pytest-д хэлдэг. “mark” нь тестэд тэмдэг тавих pytest-ийн хэрэгслүүдийн бүлэг, “parametrize” бол тэдгээрийн нэг. Үгийн бичлэгт анхаараарай: “parametrize”, “parameterize” биш.
Decorator хоёр аргумент авна. Эхнийх нь “amount, expected” гэсэн текст: хүснэгтийн баганын нэрс, таслалаар тусгаарлагдсан. Хоёр дахь нь жагсаалт: хүснэгтийн мөрүүд. Мөр бүр tuple бөгөөд баганын тоотой ижил тооны утгатай: эхнийх amount, хоёр дахь нь expected. Доор нь тест функц баганын нэрстэй яг ижил нэртэй параметрүүд авна: amount ба expected. Функцийн бие нэг мөр бөгөөд ямар ч тодорхой тоо агуулахгүй, зөвхөн “add_vat(amount) нь expected-тэй тэнцүү байх ёстой” гэсэн дүрэм.
pytest энэ функцийг уншаад хүснэгтийн мөр бүрт тусдаа тест үүсгэнэ: эхний тест amount=0, expected=0-оор, хоёр дахь нь amount=1, expected=1-ээр гэх мэт. Нэг функц, долоон тест.
Алхам 3: Ажиллуулах
Command:
pytest tests\test_prices.py -v -k add_vatГаралт:
collected 18 items / 7 deselected / 11 selected
tests/test_prices.py::test_add_vat[0-0] PASSED [ 9%]
tests/test_prices.py::test_add_vat[1-1] PASSED [ 18%]
tests/test_prices.py::test_add_vat[10-11] PASSED [ 27%]
tests/test_prices.py::test_add_vat[99-108] PASSED [ 36%]
tests/test_prices.py::test_add_vat[1000-1100] PASSED [ 45%]
tests/test_prices.py::test_add_vat[1999-2198] PASSED [ 54%]
tests/test_prices.py::test_add_vat[5000-5500] PASSED [ 63%]
tests/test_prices.py::test_add_vat_exact PASSED [ 72%]
tests/test_prices.py::test_prices_with_vat PASSED [ 81%]
tests/test_prices.py::test_prices_with_vat_empty PASSED [ 90%]
tests/test_prices.py::test_vat_price_in_list PASSED [100%]
====================== 11 passed, 7 deselected in 0.03s =======================8.1 дүгээр хичээлийн -k-ээр нэрэндээ “add_vat” гэсэн тестүүдийг сонгосон, “prices_with_vat”-ийн нэрэнд ч “vat” байдаг тул тэдгээр ч сонгогдсон. Гол нь эхний долоон мөр: тест бүрийн нэрийн ард дөрвөлжин хаалтанд тухайн мөрийн утгууд харагдаж байна. “test_add_vat[99-108]” нь “test_add_vat, amount=99, expected=108 гэсэн мөрөөр”. pytest утгуудыг зураасаар холбож тест бүрт давтагдашгүй нэр өгсөн. Энэ хаалтан доторх хэсгийг тестийн ID гэдэг. Таны тестийн тоо өмнөх хичээлүүдийн "туршаад үзээрэй" хэсгүүдээс хамаараад өөр байж болно.
Гурван тест долоо болсон ч код богиноссон. Хэрвээ дараа нь шинэ тохиолдол нэмэх шаардлага гарвал, жишээ нь маш том дүн, хүснэгтэд нэг мөр нэмэхэд л хангалттай. Шинэ функц, шинэ нэр, шинэ assert бичих шаардлагагүй.
Хүснэгт ашиглах үед уналтын тайланг унших нь илүү чухал болдог, учир нь нэг функцийн нэр дор олон тест байна. Зориуд алдаатай мөр нэмж үзье. Хэн нэгэн хүснэгтэд 999 төгрөгийн тохиолдол нэмж, “999-ийн арван хувь бараг 100, тэгэхээр 1099” гэж бодсон гэж үзье. Хүснэгтэд (1000, 1100)-ийн өмнө дараах мөрийг нэмнэ үү.
python
(999, 1099),Command:
pytestГаралт (уналтын блок ба хураангуй):
___________________________ test_add_vat[999-1099] ____________________________
amount = 999, expected = 1099
@pytest.mark.parametrize(
"amount, expected",
[
(0, 0),
(1, 1),
(10, 11),
(99, 108),
(999, 1099),
(1000, 1100),
(1999, 2198),
(5000, 5500),
],
)
def test_add_vat(amount, expected):
> assert add_vat(amount) == expected
E assert 1098 == 1099
E + where 1098 = add_vat(999)
tests\test_prices.py:21: AssertionError
=========================== short test summary info ===========================
FAILED tests/test_prices.py::test_add_vat[999-1099] - assert 1098 == 1099
========================= 1 failed, 30 passed in 0.05s ========================7.4 дүгээр хичээлийн аргаар доороос нь уншъя. Хураангуйн мөр аль тест унасныг хэлж байна: “test_add_vat[999-1099]”. Хаалтан доторх ID шууд аль мөр болохыг заана: 999 ба 1099. Хүснэгтийн бусад долоон мөр давсан тул асуудал зөвхөн энэ мөрөнд.
Уналтын блокийн хоёр шинэ онцлогийг анзаараарай. Эхнийх нь гарчгийн шууд доорх “amount = 999, expected = 1099” мөр: pytest энэ тохиолдолд параметрүүд ямар утга авсныг харуулж байна, 7.4 дүгээр хичээлийн “amount = ‘500’” мөртэй ижил. Хоёр дахь нь pytest зөвхөн тест функцийг биш, decorator-ийг ч бүхэлд нь, хүснэгтийн хамт харуулж байна. Энэ нь урт ч тустай: хүснэгтийн аль мөр асуудалтайг контекстод нь харна. Доод хэсэг танил: бодит үр дүн 1098, хүлээлт 1099, where мөр add_vat(999)-ийг тайлбарлана. Таны мөрийн дугаар 21-ээс өөр байж болно.
Одоо 7.4 дүгээр хичээлийн гол асуулт: код уу, тест үү? Гараар дахин бодъё. 999-ийн арван хувь бол 99.9. add_vat доош тайрдаг тул 99. 999 дээр 99 нэмбэл 1098. Код зөв, хүснэгтийн мөр буруу: хүснэгт бичсэн хүн тайралтыг мартжээ. Мөрийг засна уу.
python
(999, 1098),Одоо энэ мөр хүснэгтэд үнэ цэнэ нэмж байгааг анзаараарай: (99, 108) ба (1999, 2198)-тэй хамт тайралтын зан төлөвийг өөр өөр хэмжээнд баримтжуулж байна. Буруу бичигдсэн мөр засагдсаны дараа хэрэгтэй хил дээрх тохиолдол болж үлдлээ.
Мөрүүдэд нэр өгөх: ids
Тоонуудаас бүрдсэн ID ойлгомжтой, гэхдээ заримдаа мөр юуг илэрхийлж байгааг нэрээр нь хэлэх нь илүү тустай. format_price-ийн хүснэгт бичиж үзье. test_prices.py-ээс test_format_price болон 7.4 дүгээр хичээлийн test_format_price_million-ийг устгаж, оронд нь бичнэ үү.
python
@pytest.mark.parametrize(
"amount, expected",
[
(0, "0₮"),
(5, "5₮"),
(999, "999₮"),
(1000, "1,000₮"),
(20500, "20,500₮"),
(1000000, "1,000,000₮"),
],
ids=["zero", "single-digit", "below-thousand", "thousand", "typical", "million"],
)
def test_format_price(amount, expected):
assert format_price(amount) == expectedГурав дахь аргумент “ids” нь мөр бүрийн нэрсийн жагсаалт, хүснэгтийн мөрүүдтэй ижил дараалалтай, ижил тоотой. Хүснэгтийн хил дээрх тохиолдлуудыг анзаараарай: таслал хэрэггүй жижиг тоонууд, таслал гарч эхлэх яг 1000-ийн хил, хоёр таслалтай сая.
Command:
pytest tests\test_prices.py -v -k format_priceГаралт:
tests/test_prices.py::test_format_price[zero] PASSED [ 14%]
tests/test_prices.py::test_format_price[single-digit] PASSED [ 28%]
tests/test_prices.py::test_format_price[below-thousand] PASSED [ 42%]
tests/test_prices.py::test_format_price[thousand] PASSED [ 57%]
tests/test_prices.py::test_format_price[typical] PASSED [ 71%]
tests/test_prices.py::test_format_price[million] PASSED [ 85%]
tests/test_prices.py::test_format_price_has_symbol PASSED [100%]
========================= 7 passed, 15 deselected in 0.02s ====================Одоо тест бүрийн нэр юуг шалгаж байгаагаа хэлж байна. “test_format_price[thousand]” унавал таслал гарч эхлэх хилд асуудал байгаа нь кодыг ч, хүснэгтийг ч нээхгүйгээр ойлгомжтой. Нэрс нь бас -k-тэй ажилладаг: pytest -k million зөвхөн сая төгрөгийн тохиолдлыг ажиллуулна.
Нийлмэл утгатай хүснэгт
Хүснэгтийн утгууд зөвхөн тоо, текст байх албагүй. receipt_total-ийн тестүүдийг хүснэгтэд нэгтгэе. tests\test_receipt.py-ээс test_receipt_total_single_item, test_receipt_total_quantity, test_receipt_total_empty-г устгаж, оронд нь бичнэ үү. Файлын дээд хэсэгт “import pytest” байгаа эсэхийг шалгаарай.
python
@pytest.mark.parametrize(
"items, expected",
[
([], 0),
([("талх", 2000, 1)], 2200),
([("сүү", 3000, 2)], 6600),
([("талх", 2000, 1), ("сүү", 3000, 2)], 8800),
],
)
def test_receipt_total(items, expected):
assert receipt_total(items) == expectedЭнд мөр бүрийн эхний утга бүтэн баримт, барааны жагсаалт. Сүүлийн мөр шинэ тохиолдол: хоёр төрлийн бараа хамтдаа. 2000 дээр 6000 нэмбэл 8000, татвартай 8800.
Command:
pytest tests\test_receipt.py -v -k "receipt_total and not sample"Гаралт:
tests/test_receipt.py::test_receipt_total[items0-0] PASSED [ 25%]
tests/test_receipt.py::test_receipt_total[items1-2200] PASSED [ 50%]
tests/test_receipt.py::test_receipt_total[items2-6600] PASSED [ 75%]
tests/test_receipt.py::test_receipt_total[items3-8800] PASSED [100%]ID-уудыг хараарай: “items0”, “items1” гэх мэт. pytest жагсаалт зэрэг нийлмэл утгыг ID-д бүтнээр нь бичих боломжгүй тул баганын нэр дээр мөрийн дугаарыг залгадаг: items0 бол эхний мөрийн items. Энэ нь ажилладаг ч “items2-6600” гэсэн нэр аль баримт болохыг хэлдэггүй. Ийм хүснэгтэд ids нэмэх нь ялангуяа тустай, жишээ нь ids=["empty", "one-item", "quantity", "two-items"].
Нэг баганатай хүснэгт
Заримдаа хүлээгдэж буй үр дүн мөр бүрт ижил, зөвхөн оролт өөр байдаг. Жишээ нь "НӨАТ үнийг хэзээ ч бууруулдаггүй" гэсэн дүрмийг олон утгаар шалгах. Ийм үед нэг баганатай хүснэгт хангалттай бөгөөд мөр бүр tuple биш, энгийн утга байна.
python
@pytest.mark.parametrize("amount", [0, 1, 9, 10, 99, 100, 123456])
def test_vat_never_decreases(amount):
assert add_vat(amount) >= amountЭнэ тест 7.3 дугаар хичээлийн test_vat_never_decreases-ийг орлож, долоон өөр утгаар шалгадаг болно. Хуучныг нь устгаад энийг нэмж болно.
parametrize хэзээ тохиромжгүй вэ
Хүснэгт хүчирхэг ч бүх давхардлыг хүснэгт болгох нь зөв биш. Хүснэгт тохиромжтой нөхцөл: бүх мөр яг ижил шалгах аргатай, зөвхөн утгууд өөр. Хэрвээ зарим мөрөнд “==”, зарим мөрөнд “in”, зарим мөрөнд алдаа гарах ёстой бол тэдгээрийг нэг хүснэгтэд шахах гэж оролдох нь тестийн доторх if нөхцлүүд рүү хөтөлдөг бөгөөд тест өөрөө ойлгомжгүй, алдаатай болдог. Тест нь энгийн байх ёстой: тестийн доторх логик бол тестийг тестлэх хэрэгтэй болох анхны шинж. Ийм үед тусдаа тестүүд илүү зөв.
Мөн хүснэгтийн хүлээгдэж буй утгуудыг хэзээ ч кодтой ижил томъёогоор тооцоолж болохгүй. Жишээ нь (amount, amount + amount * 10 // 100) гэсэн мөрүүд бичвэл тест кодыг өөртэй нь харьцуулж байгаа хэрэг: код алдаатай бол тест ч ижил алдаатай бөгөөд үргэлж давна. Хүлээгдэж буй утга бүр бие даан, гараар бодогдсон тоо байх ёстой, 7.2 дугаар хичээлийн дүрэм хүснэгтэд ч хамаатай.
Хүсвэл 7.3 дугаар хичээлийн "туршаад үзээрэй" хэсэгт бичсэн apply_discount функцэд, эсвэл одоо шинээр бичээд, хүснэгт бүтээж үзээрэй. Эхлээд цаасан дээр хүснэгтээ бод: тэг хувь, зуун хувь, ердийн хувь, бутархай гарах тохиолдол. Бутархай гарах бол 7.3 дугаар хичээлийн pytest.approx-ийг хүснэгтийн expected-д хэрэглэж болно: assert apply_discount(price, percent) == pytest.approx(expected). Мөрүүдэд ids-ээр нэр өгч, pytest -v-ээр нэрс хэрхэн харагдахыг шалгаарай.
Баганын нэр ба параметрийн нэр зөрөх. Хэрвээ decorator-ийн текстэд “amount, expectd” гэж үсгийн алдаатай бичигдсэн бол pytest цуглуулах үедээ зогсоно.
_____________________ ERROR collecting tests/test_prices.py ___________________
In test_add_vat: function uses no argument 'expectd'“test_add_vat-д: функц ‘expectd’ гэсэн аргумент ашигладаггүй” гэсэн утгатай. pytest баганын нэр бүрийг функцийн параметртэй тааруулах гэж оролдоод олсонгүй. 8.1 дүгээр хичээлээс санаж байгаачлан ERROR нь тест ажиллаж ч эхлээгүй гэсэн утгатай. Баганын нэрс болон функцийн параметрүүдийг үсэг бүрээр нь харьцуулаарай.
Мөрийн утгын тоо буруу. Хэрвээ аль нэг мөрөнд баганын тооноос өөр тооны утга байвал, жишээ нь (1000, 1100, 5), дараах алдаа гарна.
test_add_vat: in "parametrize" the number of names (2):
['amount', 'expected']
must be equal to the number of values (3):
(1000, 1100, 5)“parametrize-д нэрсийн тоо (2) утгын тоо (3)-тай тэнцүү байх ёстой” гэсэн утгатай, мессеж асуудалтай мөрийг яг харуулдаг. Мөрийг засаад дахин ажиллуулна уу. Хаалт мартах ч ижил асуудал үүсгэдэг: (1000, 1100)-ийн оронд 1000, 1100 гэж хаалтгүй бичвэл pytest хоёр тусдаа мөр гэж ойлгоно.
“parametrize”-г буруу бичих. Англи хэлэнд энэ үгийг “parameterize” гэж бичих нь элбэг тул энэ алдаа маш түгээмэл.
Failed: Unknown 'parameterize' mark, did you mean 'parametrize'?"Танигдахгүй ‘parameterize’ тэмдэг, ‘parametrize’ гэж хэлэх гэсэн юм уу?" гэсэн утгатай. pytest энэ алдааг маш олон удаа харсан тул тусгайлан анзаардаг. pytest-ийн бичлэг “e”-гүй: parametrize.
ids-ийн тоо мөрийн тоотой таарахгүй байх. Хүснэгтэд мөр нэмсэн хэрнээ ids жагсаалтад нэр нэмэхээ мартвал pytest нэрсийн тоо мөрийн тоотой зөрж байгааг мэдэгдэх алдаа гаргана. Хүснэгтэд мөр нэмэх, хасах бүрт ids жагсаалтыг зэрэг шинэчлэх дадал гаргаарай, эсвэл ids-ийг зөвхөн мөр нь цөөн, тогтвортой хүснэгтэд хэрэглээрэй.
Нэг тохиолдлыг ажиллуулах үед дөрвөлжин хаалтны асуудал. Нэг мөрийг node id-аар ажиллуулахдаа, жишээ нь pytest tests/test_prices.py::test_format_price[million], зарим terminal дөрвөлжин хаалтыг тусгай тэмдэгт гэж ойлгодог. 3.3 дугаар хичээлийн PowerShell-ийн дөрвөлжин хаалттай замуудыг санаарай. Аюулгүй байхын тулд бүхэлд нь хашилтанд хийнэ үү: pytest "tests/test_prices.py::test_format_price[million]". Эсвэл илүү энгийн нь pytest -k million.
parametrize нь ижил бүтэцтэй, зөвхөн утгаараа ялгаатай тестүүдийг нэг хүснэгт болгон бичих боломж олгодог: тест функц шалгах арга, хүснэгт тохиолдлууд. @pytest.mark.parametrize("баганууд", [мөрүүд]) хэлбэрээр бичиж, баганын нэрстэй ижил нэртэй параметрүүдийг тест функцэд өгнө. pytest мөр бүрт тусдаа тест үүсгэж, утгуудаас нь “test_add_vat[99-108]” гэх мэт ID гаргана. Уналтын тайланд ID аль мөр унасныг, гарчгийн доорх мөр параметрүүдийн утгыг, блок бүхэл хүснэгтийг харуулна. ids=[...] мөрүүдэд утга бүхий нэр өгч, -k-ээр сонгох боломжтой болгоно, ялангуяа жагсаалт зэрэг нийлмэл утгад pytest “items0” гэх мэт ерөнхий ID гаргадаг тул тустай. Нэг баганатай хүснэгтэд мөр бүр энгийн утга байна. Хүснэгт бүх мөр ижил шалгах аргатай үед л тохиромжтой, хүлээгдэж буй утгууд кодтой ижил томъёогоор биш, бие даан бодогдсон байх ёстой. Бичлэг нь “parametrize”, “e”-гүй.
Дараагийн хичээлд одоог хүртэл тойрсон асуудлыг шийднэ: функц алдаа гаргах ёстой үед түүнийг хэрхэн тестлэх вэ? 7.4 дүгээр хичээлд format_price-д текст өгөхөд ValueError гарахыг харсан. pytest.raises нь "энэ оролтод яг энэ алдаа гарах ёстой" гэдгийг шалгах боломж олгодог бөгөөд өмнөх сургалтын raise-ийн хичээлтэй шууд холбогдоно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.