ทดสอบคลาสและวิธีการ
นอกเหนือจากการเขียนฟังก์ชันการทดสอบแล้ว Pytest ยังช่วยให้คุณสามารถใช้คลาสได้ ดังที่เราได้กล่าวไปแล้วไม่จําเป็นต้องรับช่วงมรดกและชั้นเรียนทดสอบทําตามกฎง่ายๆ สองสามข้อ การใช้คลาสช่วยให้คุณมีความยืดหยุ่นและการนํากลับมาใช้ใหม่ได้มากขึ้น อย่างที่คุณเห็นถัดไป Pytest ไม่อยู่ในแนวทางและหลีกเลี่ยงการบังคับให้คุณเขียนการทดสอบด้วยวิธีใดวิธีหนึ่ง
เช่นเดียวกับฟังก์ชัน คุณยังสามารถเขียนการยืนยันโดยใช้คําสั่ง assert ได้
สร้างคลาสทดสอบ
ลองใช้สถานการณ์จริงเพื่อดูว่าชั้นเรียนทดสอบสามารถช่วยได้อย่างไร ฟังก์ชันต่อไปนี้จะตรวจสอบว่าไฟล์ที่ระบุมี "ใช่" ในเนื้อหาหรือไม่ ถ้าเป็นเช่นนั้น จะส่งกลับ True ถ้าไฟล์ไม่มีอยู่หรือถ้าไฟล์มี "ไม่มี" ในเนื้อหา ไฟล์จะส่งกลับ False สถานการณ์นี้เป็นเรื่องปกติในงานอะซิงโครนัสที่ใช้ระบบไฟล์เพื่อระบุความสมบูรณ์
นี่คือลักษณะที่ปรากฏของฟังก์ชัน:
import os
def is_done(path):
if not os.path.exists(path):
return False
with open(path) as _f:
contents = _f.read()
if "yes" in contents.lower():
return True
elif "no" in contents.lower():
return False
ในตอนนี้ นี่คือลักษณะคลาสที่มีการทดสอบสองรายการ (หนึ่งรายการสําหรับแต่ละเงื่อนไข) ในไฟล์ที่มีชื่อว่า test_files.py มีลักษณะดังนี้:
class TestIsDone:
def test_yes(self):
with open("/tmp/test_file", "w") as _f:
_f.write("yes")
assert is_done("/tmp/test_file") is True
def test_no(self):
with open("/tmp/test_file", "w") as _f:
_f.write("no")
assert is_done("/tmp/test_file") is False
ความระมัดระวัง
วิธีทดสอบใช้เส้นทาง /tmp สําหรับไฟล์ทดสอบชั่วคราวเนื่องจากง่ายต่อการใช้สําหรับตัวอย่าง อย่างไรก็ตาม ถ้าคุณจําเป็นต้องใช้ไฟล์ชั่วคราว พิจารณาการใช้ไลบรารี เช่น tempfile ที่สามารถสร้าง (และลบ) ไฟล์เหล่านั้นได้อย่างปลอดภัย ไม่ใช่ทุกระบบที่มีไดเรกทอรี /tmp และตําแหน่งที่ตั้งอาจไม่ชั่วคราวทั้งนี้ขึ้นอยู่กับระบบปฏิบัติการ
เรียกใช้การทดสอบด้วยค่าสถานะ -v เพื่อเพิ่มความละเอียดแสดงการทดสอบที่ส่งผ่าน:
pytest -v test_files.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
cachedir: .pytest_cache
rootdir: /private/
collected 2 items
test_files.py::TestIsDone::test_yes PASSED [ 50%]
test_files.py::TestIsDone::test_no PASSED [100%]
============================== 2 passed in 0.00s ===============================
แม้ว่าการทดสอบจะผ่าน แต่ดูซ้ํา ๆ และพวกเขายังออกจากไฟล์หลังจากการทดสอบเสร็จสิ้น ก่อนที่เราจะเห็นว่าเราสามารถปรับปรุงวิชวลเหล่านั้นได้อย่างไร เรามาดูวิธีการของผู้ช่วยเหลือในส่วนถัดไปกัน
วิธีการของตัวช่วยเหลือ
ในคลาสการทดสอบ มีสองสามวิธีที่คุณสามารถใช้ในการตั้งค่าและทําให้การดําเนินการทดสอบแย่ลง Pytest ดําเนินการโดยอัตโนมัติหากกําหนดไว้ หากต้องการใช้วิธีการเหล่านี้ คุณควรทราบว่าวิธีการดังกล่าวมีลําดับและลักษณะการทํางานที่เฉพาะเจาะจง
-
setup: ดําเนินการหนึ่งครั้งก่อนการทดสอบแต่ละครั้งในคลาส -
teardown: ดําเนินการหนึ่งครั้งหลังจากการทดสอบแต่ละครั้งในคลาส -
setup_class: ดําเนินการหนึ่งครั้งก่อนการทดสอบทั้งหมดในคลาส -
teardown_class: ดําเนินการหนึ่งครั้งหลังจากการทดสอบทั้งหมดในคลาส
เมื่อการทดสอบจําเป็นต้องใช้ทรัพยากรที่คล้ายกัน (หรือเหมือนกัน) ในการทํางาน จะมีประโยชน์ในการเขียนวิธีการตั้งค่า ตามแนวคิดแล้ว การทดสอบไม่ควรทิ้งทรัพยากรไว้เมื่อเสร็จสมบูรณ์ ดังนั้นวิธีการฉีกขาดสามารถช่วยในการทดสอบการล้างข้อมูลในสถานการณ์เหล่านั้นได้
ล้าง
มาดูคลาสการทดสอบที่อัปเดตแล้วที่ล้างไฟล์หลังจากการทดสอบแต่ละครั้ง:
class TestIsDone:
def teardown(self):
if os.path.exists("/tmp/test_file"):
os.remove("/tmp/test_file")
def test_yes(self):
with open("/tmp/test_file", "w") as _f:
_f.write("yes")
assert is_done("/tmp/test_file") is True
def test_no(self):
with open("/tmp/test_file", "w") as _f:
_f.write("no")
assert is_done("/tmp/test_file") is False
เนื่องจากเราใช้วิธี teardown() คลาสการทดสอบนี้จะไม่ทิ้ง /tmp/test_file ไว้ข้างหลังอีกต่อไป
ตั้ง ค่า
การปรับปรุงอีกประการหนึ่งที่เราสามารถทํากับคลาสนี้คือการเพิ่มตัวแปรที่ชี้ไปยังไฟล์ เนื่องจากไฟล์ได้รับการประกาศในหกที่แล้ว การเปลี่ยนแปลงใด ๆ ในเส้นทางจะหมายถึงการเปลี่ยนแปลงในทุกจุดเหล่านั้น ตัวอย่างนี้แสดงให้เห็นว่าคลาสมีลักษณะอย่างไรด้วยวิธีการ setup() ที่เพิ่มที่ตรวจนับตัวแปรเส้นทาง:
class TestIsDone:
def setup(self):
self.tmp_file = "/tmp/test_file"
def teardown(self):
if os.path.exists(self.tmp_file):
os.remove(self.tmp_file)
def test_yes(self):
with open(self.tmp_file, "w") as _f:
_f.write("yes")
assert is_done(self.tmp_file) is True
def test_no(self):
with open(self.tmp_file, "w") as _f:
_f.write("no")
assert is_done(self.tmp_file) is False
วิธีการของตัวช่วยเหลือแบบกําหนดเอง
คุณสามารถสร้างวิธีการของตัวช่วยเหลือแบบกําหนดเองในคลาส วิธีการเหล่านี้ต้องไม่มีคํานําหน้าด้วยชื่อ test และไม่สามารถตั้งชื่อเป็นวิธีการตั้งค่าหรือการล้างข้อมูลได้ ใน TestIsDone คลาส เราสามารถสร้างไฟล์ชั่วคราวในตัวช่วยเหลือแบบกําหนดเองโดยอัตโนมัติ วิธีการของตัวช่วยเหลือแบบกําหนดเองอาจมีลักษณะดังตัวอย่างนี้:
def write_tmp_file(self, content):
with open(self.tmp_file, "w") as _f:
_f.write(content)
Pytest ไม่ได้ดําเนินการวิธีการ write_tmp_file() โดยอัตโนมัติ และวิธีอื่น ๆ สามารถเรียกใช้โดยตรงเพื่อบันทึกงานที่ซ้ํา ๆ เช่น การเขียนไปยังไฟล์
คลาสทั้งหมดมีลักษณะดังนี้หลังจากอัปเดตวิธีทดสอบเพื่อใช้ตัวช่วยเหลือแบบกําหนดเอง:
class TestIsDone:
def setup(self):
self.tmp_file = "/tmp/test_file"
def teardown(self):
if os.path.exists(self.tmp_file):
os.remove(self.tmp_file)
def write_tmp_file(self, content):
with open(self.tmp_file, "w") as _f:
_f.write(content)
def test_yes(self):
self.write_tmp_file("yes")
assert is_done(self.tmp_file) is True
def test_no(self):
self.write_tmp_file("no")
assert is_done(self.tmp_file) is False
เมื่อต้องการใช้คลาสแทนฟังก์ชัน
ไม่มีกฎที่เข้มงวดเกี่ยวกับ เวลาที่จะใช้คลาส แทนที่จะเป็นฟังก์ชัน คุณควรปฏิบัติตามแบบแผนในโครงการและทีมปัจจุบันที่คุณกําลังทํางานอยู่ ต่อไปนี้คือคําถามทั่วไปเพื่อถามว่าสามารถช่วยให้คุณกําหนดเวลาในการใช้ชั้นเรียนได้:
- การทดสอบของคุณจําเป็นต้องมีรหัสผู้ช่วยเหลือในการตั้งค่าหรือการล้างข้อมูลที่คล้ายกันหรือไม่
- การจัดกลุ่มการทดสอบของคุณเข้าด้วยกันสมเหตุสมผลหรือไม่
- มีการทดสอบอย่างน้อย 2-3 ครั้งในชุดการทดสอบของคุณหรือไม่
- การทดสอบของคุณสามารถใช้ประโยชน์จากชุดฟังก์ชันผู้ช่วยเหลือทั่วไปได้หรือไม่