ข้อมูลพื้นฐานเกี่ยวกับ Pytest
มาเริ่มทดสอบกับ Pytest กันเลย ดังที่เราได้กล่าวไว้ในหน่วยก่อนหน้า Pytest สามารถกําหนดค่าได้สูงและสามารถจัดการชุดการทดสอบที่ซับซ้อน แต่ไม่จําเป็นต้องใช้มากในการเริ่มเขียนการทดสอบ ในความเป็นจริงเฟรมเวิร์กที่ง่ายขึ้นช่วยให้คุณสามารถเขียนการทดสอบได้ดีขึ้น
ในตอนท้ายของส่วนนี้คุณควรมีทุกสิ่งที่คุณต้องเริ่มเขียนการทดสอบแรกของคุณและเรียกใช้ด้วย Pytest
ข้อ ตกลง
ก่อนที่จะดําดิ่งลงไปในการทดสอบการเขียนเราต้องครอบคลุมบางมาตรฐานการทดสอบที่ Pytest อาศัย
ไม่มีกฎที่ยากลําบากเกี่ยวกับไฟล์ทดสอบ ไดเรกทอรีทดสอบ หรือเค้าโครงการทดสอบทั่วไปใน Python เมื่อรู้กฎเหล่านี้คุณสามารถใช้ประโยชน์จากการค้นหาการทดสอบโดยอัตโนมัติและการดําเนินการโดยไม่จําเป็นต้องมีการกําหนดค่าเพิ่มเติม
ทดสอบไดเรกทอรีและไฟล์ทดสอบ
ไดเรกทอรีหลักสําหรับการทดสอบคือการทดสอบ ไดเรกทอรี คุณสามารถวางไดเรกทอรีนี้ที่ระดับรากของโครงการ แต่ก็ไม่ใช่เรื่องผิดปกติที่จะเห็นควบคู่ไปกับมอดูลโค้ด
โน้ต
ในมอดูลนี้ เราจะเริ่มต้นใช้ การทดสอบ ที่รากของโครงการ
มาดูกันว่ารากของโครงการ Python ขนาดเล็กชื่อ jformat มีลักษณะอย่างไร:
.
├── README.md
├── jformat
│ ├── __init__.py
│ └── main.py
├── setup.py
└── tests
└── test_main.py
การทดสอบ ไดเรกทอรีอยู่ที่รากของโครงการที่มีไฟล์ทดสอบเดียว ในกรณีนี้ จะเรียกว่าไฟล์ทดสอบ test_main.py ตัวอย่างนี้แสดงให้เห็นถึงแบบแผนสําคัญสองแบบ:
- ใช้การทดสอบ ไดเรกทอรีเพื่อวางไฟล์ทดสอบและไดเรกทอรีการทดสอบที่ซ้อนกัน
- ไฟล์ทดสอบคํานําหน้าด้วย ทดสอบ คํานําหน้าบ่งชี้ว่าไฟล์ประกอบด้วยโค้ดทดสอบ
ความระมัดระวัง
หลีกเลี่ยงการใช้ test (ฟอร์มเอกพจน์) เป็นชื่อไดเรกทอรี ชื่อ test เป็นโมดูล Python ดังนั้นการสร้างไดเรกทอรีที่ชื่อเดียวกันจะแทนที่มัน ใช้ tests พหูพจน์แทนเสมอ
ฟังก์ชัน Test
อาร์กิวเมนต์ที่แข็งแกร่งสําหรับการใช้ Pytest คือช่วยให้คุณสามารถเขียนฟังก์ชันการทดสอบได้ คล้ายกับไฟล์ทดสอบ ฟังก์ชันทดสอบต้องขึ้นต้นด้วย test_ คํานําหน้า test_ ช่วยให้แน่ใจว่า Pytest จะรวบรวมการทดสอบและดําเนินการ
นี่คือลักษณะของฟังก์ชันการทดสอบอย่างง่าย:
def test_main():
assert "a string value" == "a string value"
โน้ต
หากคุณคุ้นเคยกับ unittestอาจเป็นเรื่องน่าประหลาดใจที่เห็นการใช้ assert ในฟังก์ชันการทดสอบ เราครอบคลุม asserts ธรรมดาในรายละเอียดเพิ่มเติมในภายหลัง แต่ด้วย Pytest คุณจะได้รับการรายงานความล้มเหลวมากมายพร้อมข้อความแฝงธรรมดา
ทดสอบคลาสและวิธีการทดสอบ
คล้ายกับหลักทั่วไปสําหรับไฟล์และฟังก์ชันคลาสการทดสอบและวิธีการทดสอบใช้หลักทั่วไปต่อไปนี้:
- คลาสการทดสอบจะถูกขึ้นต้นด้วย
Test - วิธีการทดสอบมีคํานําหน้า
test_
ความแตกต่างที่สําคัญของไลบรารี unittest ของ Python คือไม่จําเป็นต้องรับช่วงมรดก
ตัวอย่างต่อไปนี้ใช้คํานําหน้าเหล่านี้และมาตรฐานการตั้งชื่อ Python อื่น ๆ สําหรับคลาสและวิธีการ ซึ่งแสดงให้เห็นถึงระดับการทดสอบขนาดเล็กที่กําลังตรวจสอบชื่อผู้ใช้ในแอปพลิเคชัน
class TestUser:
def test_username(self):
assert default() == "default username"
เรียกใช้การทดสอบ
Pytest เป็นทั้งเฟรมเวิร์กการทดสอบและโปรแกรมรันการทดสอบ โปรแกรมรันการทดสอบเป็นคําสั่งที่สามารถดําเนินการได้ในบรรทัดคําสั่ง (ในระดับสูง) สามารถ:
- ดําเนินการรวบรวมการทดสอบโดยการค้นหาไฟล์ทดสอบคลาสทดสอบและฟังก์ชันทดสอบทั้งหมดสําหรับการทดสอบการเรียกใช้
- เริ่มต้นการทดสอบการทํางานโดยการดําเนินการทดสอบทั้งหมด
- ติดตามความล้มเหลว ข้อผิดพลาด และการทดสอบการส่งผ่าน
- ให้การรายงานที่หลากหลายในตอนท้ายของการทดสอบการใช้งาน
โน้ต
เนื่องจาก Pytest เป็นไลบรารีภายนอก จึงต้อง ติดตั้งเพื่อใช้งาน
กําหนดเนื้อหาเหล่านี้ในไฟล์ test_main.py เราจะเห็นว่า Pytest ทํางานอย่างไรต่อการทดสอบ:
# contents of test_main.py file
def test_main():
assert True
ในบรรทัดคําสั่งในเส้นทางเดียวกันกับที่มีไฟล์ test_main.py เราสามารถเรียกใช้ pytest ปฏิบัติการได้:
$ pytest
=========================== test session starts ============================
platform -- Python 3.10.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /private/tmp/project
collected 1 item
test_main.py . [100%]
============================ 1 passed in 0.00s =============================
ในเบื้องหลัง Pytest จะรวบรวมการทดสอบตัวอย่างในไฟล์ทดสอบโดยไม่จําเป็นต้องมีการกําหนดค่าใด ๆ
คําสั่ง assert ที่มีประสิทธิภาพ
จนถึงตอนนี้ ตัวอย่างการทดสอบของเราทั้งหมดใช้การโทร assert ธรรมดา โดยปกติแล้วใน Python assert คําสั่ง ไม่ได้ถูกใช้สําหรับการทดสอบเนื่องจากไม่มีการรายงานที่เหมาะสมเมื่อการยืนยันล้มเหลว อย่างไรก็ตาม Pytest ไม่มีข้อจํากัดนี้ ในเบื้องหลัง Pytest เปิดใช้งานคําสั่งเพื่อทําการเปรียบเทียบที่หลากหลายโดยไม่ต้องบังคับให้ผู้ใช้เขียนโค้ดเพิ่มเติมหรือกําหนดค่าใด ๆ
ด้วยการใช้คําสั่งธรรมดา assert คุณสามารถใช้ตัวดําเนินการของ Python ได้ ตัวอย่างเช่น ><, , !=, >=หรือ<= ตัวดําเนินการของ Python ทั้งหมดถูกต้อง ความสามารถนี้อาจเป็นคุณลักษณะที่สําคัญที่สุดอย่างเดียวของ Pytest: คุณไม่จําเป็นต้องเรียนรู้ไวยากรณ์ใหม่เพื่อเขียนการยืนยัน
มาดูวิธีที่แปลเมื่อจัดการกับการเปรียบเทียบทั่วไปกับวัตถุ Python ในกรณีนี้ เรามาดูรายงานความล้มเหลวเมื่อเปรียบเทียบสตริงที่ยาว:
================================= FAILURES =================================
____________________________ test_long_strings _____________________________
def test_long_strings():
left = "this is a very long strings to be compared with another long string"
right = "This is a very long string to be compared with another long string"
> assert left == right
E AssertionError: assert 'this is a ve...r long string' == 'This is a ve...r long string'
E - This is a very long string to be compared with another long string
E ? ^
E + this is a very long strings to be compared with another long string
E ? ^ +
test_main.py:4: AssertionError
Pytest แสดงบริบทที่มีประโยชน์เกี่ยวกับความล้มเหลว: การกําหนดปลอกที่ไม่ถูกต้องที่จุดเริ่มต้นของสตริงและอักขระพิเศษในคํา แต่นอกเหนือจากสตริงแล้ว Pytest สามารถช่วยในการวัตถุและโครงสร้างข้อมูลอื่น ๆ ได้ ตัวอย่างเช่น วิธีการทํางานกับรายการมีดังนี้:
________________________________ test_lists ________________________________
def test_lists():
left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
right = ["sugar", "coffee", "wheat", "salt", "water", "milk"]
> assert left == right
E AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'co...ater', 'milk']
E At index 1 diff: 'wheat' != 'coffee'
E Full diff:
E - ['sugar', 'coffee', 'wheat', 'salt', 'water', 'milk']
E ? ---------
E + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E ? +++++++++
test_main.py:9: AssertionError
รายงานนี้ระบุว่าดัชนี 1 (หน่วยข้อมูลที่สองในรายการ) แตกต่างกัน ไม่เพียงระบุหมายเลขดัชนีเท่านั้น แต่ยังแสดงถึงความล้มเหลวด้วย นอกเหนือจากการเปรียบเทียบหน่วยข้อมูล แล้วยังสามารถรายงานได้หากรายการหายไป และให้ข้อมูลที่สามารถบอกให้คุณทราบว่ารายการใดอาจเป็นได้ ในกรณีต่อไปนี้ที่ "milk":
________________________________ test_lists ________________________________
def test_lists():
left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
right = ["sugar", "wheat", "salt", "water", "milk"]
> assert left == right
E AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'wh...ater', 'milk']
E At index 2 diff: 'coffee' != 'salt'
E Left contains one more item: 'milk'
E Full diff:
E - ['sugar', 'wheat', 'salt', 'water', 'milk']
E + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E ? ++++++++++
test_main.py:9: AssertionError
สุดท้าย มาดูกันว่ามันทํางานอย่างไรกับพจนานุกรม การเปรียบเทียบพจนานุกรมขนาดใหญ่สองรายการอาจครอบงําหากมีความล้มเหลว แต่ Pytest ทํางานที่โดดเด่นในการให้บริบทและระบุความล้มเหลว:
____________________________ test_dictionaries _____________________________
def test_dictionaries():
left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
> assert left == right
E AssertionError: assert {'county': 'F...rry Ln.', ...} == {'county': 'F...ry Lane', ...}
E Omitting 3 identical items, use -vv to show
E Differing items:
E {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E {'number': 39} != {'number': 38}
E Full diff:
E {
E 'county': 'Frett',...
E
E ...Full output truncated (12 lines hidden), use '-vv' to show
ในการทดสอบนี้ มีสองความล้มเหลวในพจนานุกรม หนึ่งคือค่า "street" แตกต่างกัน และอีกค่าหนึ่งคือ "number" ไม่ตรงกัน
Pytest ตรวจหาความแตกต่างเหล่านี้ได้อย่างถูกต้อง (แม้ว่าจะเป็นความล้มเหลวหนึ่งในการทดสอบเดียว) เนื่องจากพจนานุกรมประกอบด้วยรายการจํานวนมาก Pytest จึงละเว้นส่วนที่เหมือนกันและแสดงเนื้อหาที่เกี่ยวข้องเท่านั้น มาดูกันว่าจะเกิดอะไรขึ้นถ้าเราใช้ธง -vv ที่แนะนําเพื่อเพิ่มความฟุ่มเฟี้ยวในผลลัพธ์:
____________________________ test_dictionaries _____________________________
def test_dictionaries():
left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
> assert left == right
E AssertionError: assert {'county': 'Frett',\n 'number': 39,\n 'state': 'Nevada',\n 'street': 'Ferry Ln.',\n 'zipcode': 30877} == {'county': 'Frett',\n 'number': 38,\n 'state': 'Nevada',\n 'street': 'Ferry Lane',\n 'zipcode': 30877}
E Common items:
E {'county': 'Frett', 'state': 'Nevada', 'zipcode': 30877}
E Differing items:
E {'number': 39} != {'number': 38}
E {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E Full diff:
E {
E 'county': 'Frett',
E - 'number': 38,
E ? ^
E + 'number': 39,
E ? ^
E 'state': 'Nevada',
E - 'street': 'Ferry Lane',
E ? - ^
E + 'street': 'Ferry Ln.',
E ? ^
E 'zipcode': 30877,
E }
โดยการเรียกใช้ pytest -vvการรายงานจะเพิ่มปริมาณรายละเอียดและให้การเปรียบเทียบที่ละเอียด รายงานนี้ไม่เพียงตรวจหาและแสดงความล้มเหลวเท่านั้น แต่จะช่วยให้คุณสามารถทําการเปลี่ยนแปลงเพื่อแก้ไขปัญหาได้อย่างรวดเร็ว