คุณกำลังใช้pytestงานซึ่งจะช่วยให้คุณมีตัวเลือกมากมายในการโต้ตอบกับการทดสอบที่ล้มเหลว มันทำให้คุณมีตัวเลือกบรรทัดคำสั่งและตะขอหลายอย่างเพื่อให้เป็นไปได้ ฉันจะอธิบายวิธีใช้แต่ละจุดที่คุณสามารถปรับแต่งเพื่อให้เหมาะกับความต้องการในการดีบักเฉพาะของคุณ
ฉันจะไปยังตัวเลือกที่แปลกใหม่มากขึ้นซึ่งจะช่วยให้คุณสามารถข้ามการยืนยันที่เฉพาะเจาะจงทั้งหมดถ้าคุณรู้สึกว่าคุณต้อง
จัดการข้อยกเว้นไม่ยืนยัน
โปรดทราบว่าการทดสอบที่ล้มเหลวจะไม่หยุดโดยปกติ pytest เฉพาะในกรณีที่คุณเปิดใช้งานการแจ้งอย่างชัดเจนให้ออกหลังจากความล้มเหลวจำนวนหนึ่ง นอกจากนี้การทดสอบล้มเหลวเนื่องจากมีการยกข้อยกเว้น assertเพิ่มขึ้นAssertionErrorแต่นั่นไม่ใช่ข้อยกเว้นเพียงอย่างเดียวที่จะทำให้การทดสอบล้มเหลว! assertคุณต้องการที่จะควบคุมวิธีการข้อยกเว้นได้รับการจัดการไม่เปลี่ยนแปลง
อย่างไรก็ตามการยืนยันที่ล้มเหลวจะสิ้นสุดการทดสอบแต่ละรายการ นั่นเป็นเพราะเมื่อมีการยกข้อยกเว้นนอกtry...exceptบล็อก Python จะคลายเฟรมฟังก์ชันปัจจุบันและจะไม่มีการย้อนกลับไป
ฉันไม่คิดว่านั่นคือสิ่งที่คุณต้องการตัดสินโดยคำอธิบายของคุณเกี่ยว_assertCustom()กับความพยายามในการยืนยันอีกครั้ง แต่ฉันจะพูดถึงตัวเลือกของคุณต่อไป
การดีบักโพสต์ชันสูตรใน pytest ด้วย pdb
สำหรับตัวเลือกต่าง ๆ เพื่อจัดการกับความล้มเหลวในดีบักเกอร์ฉันจะเริ่มต้นด้วย--pdbสวิตช์บรรทัดคำสั่งซึ่งจะเปิดพร้อมท์การดีบักมาตรฐานเมื่อการทดสอบล้มเหลว
$ mkdir demo
$ touch demo/__init__.py
$ cat << EOF > demo/test_foo.py
> def test_ham():
> assert 42 == 17
> def test_spam():
> int("Vikings")
> EOF
$ pytest demo/test_foo.py --pdb
[ ... ]
test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(2)test_ham()
-> assert 42 == 17
(Pdb) q
Exit: Quitting debugger
[ ... ]
ด้วยสวิตช์นี้เมื่อการทดสอบล้มเหลว pytest เริ่มชันสูตรศพเซสชั่นการแก้จุดบกพร่อง นี่คือสิ่งที่คุณต้องการอย่างแท้จริง เพื่อหยุดโค้ดที่จุดที่ทำการทดสอบที่ล้มเหลวและเปิดดีบักเกอร์เพื่อดูสถานะการทดสอบของคุณ คุณสามารถโต้ตอบกับตัวแปรท้องถิ่นของการทดสอบ, globals และ localals และ globals ของทุกเฟรมในสแต็ก
ที่นี่ pytest ช่วยให้คุณควบคุมได้อย่างสมบูรณ์ว่าจะออกจากจุดนี้หรือไม่: ถ้าคุณใช้qคำสั่ง quit จากนั้น pytest จะออกจากการรันด้วยการใช้cเพื่อดำเนินการต่อจะส่งคืนการควบคุมไปยัง pytest และทำการทดสอบครั้งต่อไป
ใช้ดีบักเกอร์สำรอง
คุณไม่ได้ผูกพันกับpdbดีบักเกอร์สำหรับสิ่งนี้ คุณสามารถตั้งค่าดีบักเกอร์ที่แตกต่างด้วย--pdbclsสวิตช์ การใช้งานที่pdb.Pdb()เข้ากันได้ใด ๆจะทำงานได้รวมถึงการใช้งานดีบักเกอร์ IPythonหรือดีบั๊ก Python อื่น ๆ ส่วนใหญ่ (ตัวดีบัก pudbต้องการ-sสวิตช์ที่ใช้หรือปลั๊กอินพิเศษ ) สวิตช์ใช้โมดูลและคลาสเช่นpudbคุณสามารถใช้:
$ pytest -s --pdb --pdbcls=pudb.debugger:Debugger
คุณสามารถใช้คุณสมบัตินี้ในการเขียนระดับกระดาษห่อของคุณเองรอบPdbที่เพียงแค่ส่งกลับทันทีถ้าล้มเหลวที่เฉพาะเจาะจงไม่ได้เป็นสิ่งที่คุณมีความสนใจใน. pytestใช้Pdb()เหมือนpdb.post_mortem()ไม่ :
p = Pdb()
p.reset()
p.interaction(None, t)
นี่tคือวัตถุย้อนกลับ เมื่อp.interaction(None, t)ส่งคืนให้pytestดำเนินการทดสอบต่อไปเว้นแต่ p.quittingจะตั้งค่าเป็นTrue(ที่จุด pytest แล้วออก)
นี่คือตัวอย่างการใช้งานที่พิมพ์ออกมาว่าเราปฏิเสธที่จะแก้ไขข้อบกพร่องและส่งคืนทันทียกเว้นกรณีที่การทดสอบยกขึ้นValueErrorบันทึกเป็นdemo/custom_pdb.py:
import pdb, sys
class CustomPdb(pdb.Pdb):
def interaction(self, frame, traceback):
if sys.last_type is not None and not issubclass(sys.last_type, ValueError):
print("Sorry, not interested in this failure")
return
return super().interaction(frame, traceback)
เมื่อฉันใช้สิ่งนี้กับการสาธิตด้านบนนี่คือผลลัพธ์ (อีกครั้งเพื่อความกะทัดรัด):
$ pytest test_foo.py -s --pdb --pdbcls=demo.custom_pdb:CustomPdb
[ ... ]
def test_ham():
> assert 42 == 17
E assert 42 == 17
test_foo.py:2: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Sorry, not interested in this failure
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def test_spam():
> int("Vikings")
E ValueError: invalid literal for int() with base 10: 'Vikings'
test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb)
วิปัสสนาข้างต้นsys.last_typeเพื่อตรวจสอบว่าความล้มเหลวคือ 'น่าสนใจ'
อย่างไรก็ตามฉันไม่สามารถแนะนำตัวเลือกนี้ได้นอกเสียจากว่าคุณต้องการเขียนดีบักเกอร์ของคุณเองโดยใช้ tkInter หรือสิ่งที่คล้ายกัน โปรดทราบว่านั่นเป็นงานที่ยิ่งใหญ่
การกรองความล้มเหลว; เลือกและเลือกเวลาที่จะเปิดดีบักเกอร์
ระดับถัดไปคือการดีบัก pytest และการโต้ตอบการขอ ; สิ่งเหล่านี้เป็นจุดเชื่อมโยงสำหรับการปรับแต่งพฤติกรรมเพื่อแทนที่หรือปรับปรุงวิธีการจัดการโดยปกติของ pytest เช่นการจัดการข้อยกเว้นหรือการป้อนดีบักเกอร์ผ่านpdb.set_trace()หรือbreakpoint()(Python 3.7 หรือใหม่กว่า)
การนำไปใช้ภายในของ hook นี้รับผิดชอบการพิมพ์>>> entering PDB >>>แบนเนอร์ด้านบนด้วยดังนั้นการใช้ hook นี้เพื่อป้องกันไม่ให้ดีบักเกอร์ทำงานหมายความว่าคุณจะไม่เห็นผลลัพธ์นี้เลย คุณสามารถมีตะขอของคุณเองจากนั้นมอบหมายให้ตะขอเดิมเมื่อความล้มเหลวในการทดสอบนั้น 'น่าสนใจ' และดังนั้นความล้มเหลวในการทดสอบตัวกรองจึงไม่ขึ้นอยู่กับดีบักเกอร์ที่คุณใช้! คุณสามารถเข้าถึงการดำเนินงานภายในโดยการเข้าถึงโดยใช้ชื่อ ; ปลั๊กอิน hook ภายในสำหรับชื่อpdbinvokeนี้ ในการป้องกันไม่ให้ทำงานคุณจำเป็นต้องยกเลิกการลงทะเบียนแต่บันทึกการอ้างอิงเราสามารถเรียกมันได้โดยตรงตามต้องการ
นี่คือตัวอย่างการใช้งานของ hook เช่น; คุณสามารถวางสิ่งนี้ลงในปลั๊กอินตำแหน่งใดก็ได้ที่โหลดจาก ; ฉันใส่ไว้ในdemo/conftest.py:
import pytest
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
# unregister returns the unregistered plugin
pdbinvoke = config.pluginmanager.unregister(name="pdbinvoke")
if pdbinvoke is None:
# no --pdb switch used, no debugging requested
return
# get the terminalreporter too, to write to the console
tr = config.pluginmanager.getplugin("terminalreporter")
# create or own plugin
plugin = ExceptionFilter(pdbinvoke, tr)
# register our plugin, pytest will then start calling our plugin hooks
config.pluginmanager.register(plugin, "exception_filter")
class ExceptionFilter:
def __init__(self, pdbinvoke, terminalreporter):
# provide the same functionality as pdbinvoke
self.pytest_internalerror = pdbinvoke.pytest_internalerror
self.orig_exception_interact = pdbinvoke.pytest_exception_interact
self.tr = terminalreporter
def pytest_exception_interact(self, node, call, report):
if not call.excinfo. errisinstance(ValueError):
self.tr.write_line("Sorry, not interested!")
return
return self.orig_exception_interact(node, call, report)
ปลั๊กอินข้างต้นใช้ภายในTerminalReporterปลั๊กอินที่จะเขียนออกเส้นไปยังสถานี; สิ่งนี้ทำให้ตัวล้างเอาต์พุตเมื่อใช้รูปแบบสถานะการทดสอบขนาดกะทัดรัดเริ่มต้นและช่วยให้คุณสามารถเขียนสิ่งต่าง ๆ ไปยังเทอร์มินัลได้แม้จะเปิดใช้งานการบันทึกเอาต์พุต
ตัวอย่างจะลงทะเบียนวัตถุปลั๊กอินด้วยpytest_exception_interacthook ผ่านตะขออื่นpytest_configure()แต่ให้แน่ใจว่ามันทำงานช้าพอ (ใช้@pytest.hookimpl(trylast=True)) เพื่อที่จะสามารถยกเลิกการลงทะเบียนpdbinvokeปลั๊กอินภายใน เมื่อเบ็ดเรียกว่าตัวอย่างทดสอบกับcall.exceptinfoวัตถุ ; คุณสามารถตรวจสอบโหนดหรือรายงานได้เช่นกัน
เมื่อโค้ดตัวอย่างข้างต้นเข้าแทนที่ความล้มเหลวในdemo/conftest.pyการtest_hamทดสอบจะถูกละเว้นเฉพาะtest_spamความล้มเหลวในการทดสอบซึ่งเพิ่มขึ้นValueErrorส่งผลให้เกิดการเปิดพร้อมท์การดีบัก:
$ pytest demo/test_foo.py --pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!
demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def test_spam():
> int("Vikings")
E ValueError: invalid literal for int() with base 10: 'Vikings'
demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
-> int("Vikings")
(Pdb)
หากต้องการปรับใหม่อีกครั้งวิธีการข้างต้นมีข้อดีเพิ่มเติมที่คุณสามารถรวมเข้ากับดีบักเกอร์ที่ทำงานกับ pytestรวมถึง pudb หรือดีบักเกอร์ IPython:
$ pytest demo/test_foo.py --pdb --pdbcls=IPython.core.debugger:Pdb
[ ... ]
demo/test_foo.py F
Sorry, not interested!
demo/test_foo.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def test_spam():
> int("Vikings")
E ValueError: invalid literal for int() with base 10: 'Vikings'
demo/test_foo.py:4: ValueError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /.../demo/test_foo.py(4)test_spam()
1 def test_ham():
2 assert 42 == 17
3 def test_spam():
----> 4 int("Vikings")
ipdb>
นอกจากนี้ยังมีบริบทอื่น ๆ อีกมากมายเกี่ยวกับการทดสอบที่เรียกใช้ (ผ่านการnodeโต้แย้ง) และการเข้าถึงโดยตรงไปยังข้อยกเว้นที่เกิดขึ้น (ผ่านcall.excinfo ExceptionInfoอินสแตนซ์)
โปรดทราบว่าปลั๊กอินดีบักเกอร์ pytest เฉพาะ (เช่นpytest-pudbหรือpytest-pycharm) ลงทะเบียนpytest_exception_interacthooksp ของตนเอง การใช้งานที่สมบูรณ์ยิ่งขึ้นจะต้องวนซ้ำปลั๊กอินทั้งหมดใน plugin-manager เพื่อแทนที่ปลั๊กอินใด ๆ โดยอัตโนมัติโดยใช้config.pluginmanager.list_name_pluginและhasattr()ทดสอบแต่ละปลั๊กอิน
ทำให้ความล้มเหลวหายไปโดยสิ้นเชิง
แม้ว่าสิ่งนี้จะช่วยให้คุณสามารถควบคุมการดีบักการทดสอบที่ล้มเหลวได้อย่างสมบูรณ์ แต่ก็ยังทำให้การทดสอบนั้นล้มเหลวแม้ว่าคุณจะเลือกที่จะไม่เปิดตัวดีบั๊กสำหรับการทดสอบที่ได้รับก็ตาม pytest_runtest_call()หากคุณต้องการที่จะทำให้ความล้มเหลวหายไปโดยสิ้นเชิงคุณสามารถทำให้การใช้ตะขอที่แตกต่างกัน
เมื่อ pytest ทำการทดสอบมันจะทำการทดสอบผ่านทาง hook ด้านบนซึ่งคาดว่าจะคืนNoneหรือเพิ่มข้อยกเว้น จากรายงานนี้ถูกสร้างขึ้นเป็นทางเลือกรายการบันทึกถูกสร้างขึ้นและหากการทดสอบล้มเหลวpytest_exception_interact()เบ็ดดังกล่าวจะถูกเรียกว่า ดังนั้นสิ่งที่คุณต้องทำคือเปลี่ยนสิ่งที่ผลลัพธ์ที่ตะขอนี้ผลิต แทนที่จะเป็นข้อยกเว้นไม่ควรส่งคืนสิ่งใดเลย
วิธีที่ดีที่สุดที่จะทำคือการใช้กระดาษห่อเบ็ด ฮุกห่อไม่จำเป็นต้องทำงานจริง แต่แทนที่จะได้รับโอกาสที่จะแก้ไขสิ่งที่เกิดขึ้นกับผลของตะขอ สิ่งที่คุณต้องทำคือเพิ่มบรรทัด:
outcome = yield
ในตะขอเสื้อคลุมการดำเนินงานและคุณจะได้รับการเข้าถึงผลเบ็ดoutcome.excinfoรวมทั้งยกเว้นการทดสอบผ่าน แอ็ตทริบิวต์นี้ถูกตั้งค่าเป็น tuple ของ (ชนิด, อินสแตนซ์, การติดตามกลับ) หากข้อยกเว้นถูกยกขึ้นในการทดสอบ อีกวิธีหนึ่งคุณสามารถโทรoutcome.get_result()และใช้การtry...exceptจัดการมาตรฐาน
ดังนั้นคุณจะทำการทดสอบผ่านที่ล้มเหลวได้อย่างไร คุณมี 3 ตัวเลือกพื้นฐาน:
- คุณสามารถทำเครื่องหมายการทดสอบว่าเป็นความล้มเหลวที่คาดไว้โดยการโทร
pytest.xfail()ใน wrapper
- คุณสามารถทำเครื่องหมายรายการเป็นข้าม
pytest.skip()ซึ่งอ้างว่าการทดสอบก็ไม่เคยทำงานในสถานที่แรกโดยการเรียก
- คุณสามารถลบข้อยกเว้นโดยใช้
outcome.force_result()วิธีการ ; ตั้งค่าผลลัพธ์เป็นรายการว่างที่นี่ (หมายถึง: hook ที่ลงทะเบียนไม่ได้ทำอะไรนอกจากNone) และข้อยกเว้นจะถูกล้างทั้งหมด
สิ่งที่คุณใช้นั้นขึ้นอยู่กับคุณ ตรวจสอบให้แน่ใจว่าได้ตรวจสอบผลลัพธ์สำหรับการทดสอบที่ข้ามและคาดว่าจะล้มเหลวก่อนเนื่องจากคุณไม่จำเป็นต้องจัดการกับกรณีเหล่านั้นราวกับว่าการทดสอบล้มเหลว คุณสามารถเข้าถึงข้อยกเว้นพิเศษเพิ่มตัวเลือกเหล่านี้ผ่านทางและpytest.skip.Exceptionpytest.xfail.Exception
ต่อไปนี้เป็นตัวอย่างการใช้งานซึ่งทำเครื่องหมายการทดสอบที่ล้มเหลวซึ่งไม่ได้เพิ่มValueErrorดังที่ถูกข้าม :
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
outcome = yield
try:
outcome.get_result()
except (pytest.xfail.Exception, pytest.skip.Exception, pytest.exit.Exception):
raise # already xfailed, skipped or explicit exit
except ValueError:
raise # not ignoring
except (pytest.fail.Exception, Exception):
# turn everything else into a skip
pytest.skip("[NOTRUN] ignoring everything but ValueError")
เมื่อใส่ในconftest.pyผลลัพธ์จะกลายเป็น:
$ pytest -r a demo/test_foo.py
============================= test session starts =============================
platform darwin -- Python 3.8.0, pytest-3.10.0, py-1.7.0, pluggy-0.8.0
rootdir: ..., inifile:
collected 2 items
demo/test_foo.py sF [100%]
=================================== FAILURES ===================================
__________________________________ test_spam ___________________________________
def test_spam():
> int("Vikings")
E ValueError: invalid literal for int() with base 10: 'Vikings'
demo/test_foo.py:4: ValueError
=========================== short test summary info ============================
FAIL demo/test_foo.py::test_spam
SKIP [1] .../demo/conftest.py:12: [NOTRUN] ignoring everything but ValueError
===================== 1 failed, 1 skipped in 0.07 seconds ======================
ฉันใช้-r aธงเพื่อให้ชัดเจนยิ่งขึ้นซึ่งtest_hamถูกข้ามไปแล้ว
หากคุณแทนที่การpytest.skip()โทรด้วยpytest.xfail("[XFAIL] ignoring everything but ValueError")การทดสอบจะถูกทำเครื่องหมายว่าเป็นความล้มเหลวที่คาดไว้:
[ ... ]
XFAIL demo/test_foo.py::test_ham
reason: [XFAIL] ignoring everything but ValueError
[ ... ]
และใช้outcome.force_result([])เครื่องหมายตามที่ผ่าน:
$ pytest -v demo/test_foo.py # verbose to see individual PASSED entries
[ ... ]
demo/test_foo.py::test_ham PASSED [ 50%]
มันขึ้นอยู่กับคุณว่าคนไหนที่เหมาะกับการใช้งานของคุณมากที่สุด สำหรับskip()และxfail()ฉันเลียนแบบรูปแบบข้อความมาตรฐาน (นำหน้าด้วย[NOTRUN]หรือ[XFAIL]) แต่คุณมีอิสระที่จะใช้รูปแบบข้อความอื่น ๆ ที่คุณต้องการ
ในทั้งสามกรณี pytest จะไม่เปิดดีบักเกอร์สำหรับการทดสอบที่คุณแก้ไขผลลัพธ์โดยใช้วิธีนี้
การปรับเปลี่ยนข้อความยืนยันแต่ละรายการ
หากคุณต้องการแก้ไขassertการทดสอบภายในการทดสอบคุณจะตั้งค่าตัวเองสำหรับการทำงานมากขึ้น ใช่มันเป็นไปได้ทางเทคนิคแต่เพียงแค่เขียนรหัสใหม่ที่ Python จะดำเนินการในเวลารวบรวมเท่านั้น
เมื่อคุณใช้pytestงานจริงสิ่งนี้กำลังดำเนินการอยู่ Pytest ปรับเปลี่ยนassertงบเพื่อให้คุณได้มากขึ้นเมื่อบริบทอ้างของคุณล้มเหลว ; ดูโพสต์บล็อกนี้สำหรับภาพรวมที่ดีของสิ่งที่จะถูกดำเนินการเช่นเดียวกับรหัสที่มา_pytest/assertion/rewrite.py โปรดทราบว่าโมดูลนั้นมีความยาวเกิน 1k บรรทัดและต้องการให้คุณเข้าใจว่าโครงสร้างแบบนามธรรมของ Pythonทำงานอย่างไร หากคุณทำเช่นนั้นคุณสามารถปรับแต่งโมดูลนั้นเพื่อเพิ่มการแก้ไขของคุณเองที่นั่นรวมถึงการล้อมรอบassertด้วยtry...except AssertionError:ตัวจัดการ
อย่างไรก็ตามคุณไม่สามารถปิดใช้งานหรือเพิกเฉยการอ้างสิทธิ์ที่เลือกได้เนื่องจากข้อความสั่งที่ตามมาอาจขึ้นอยู่กับสถานะ (การจัดเรียงวัตถุเฉพาะชุดตัวแปร ฯลฯ ) ที่การยืนยันที่ข้ามนั้นมีไว้เพื่อป้องกัน หากการทดสอบการยืนยันที่fooไม่ได้Noneมีการยืนยันในภายหลังอาศัยfoo.barอยู่แล้วคุณก็จะวิ่งเข้าไปที่AttributeErrorนั่น ฯลฯ ทำติดเพื่อยกข้อยกเว้นอีกครั้งถ้าคุณจำเป็นต้องไปเส้นทางนี้
ฉันจะไม่เข้าไปดูรายละเอียดเพิ่มเติมเกี่ยวกับการเขียนใหม่assertsที่นี่เนื่องจากฉันไม่คิดว่านี่จะเป็นสิ่งที่ควรทำไม่ได้รับปริมาณงานที่เกี่ยวข้อง จุดของความล้มเหลวการยืนยันอยู่แล้ว
โปรดทราบว่าหากคุณต้องการทำสิ่งนี้คุณไม่จำเป็นต้องใช้eval()(ซึ่งไม่สามารถใช้งานassertได้เป็นคำสั่งดังนั้นคุณจะต้องใช้exec()แทน) และคุณจะไม่ต้องทำการยืนยันสองครั้ง สามารถนำไปสู่ปัญหาหากการแสดงออกที่ใช้ในสถานะการเปลี่ยนแปลงการยืนยัน) คุณต้องฝังast.Assertโหนดภายในast.Tryโหนดแทนและแนบตัวจัดการยกเว้นที่ใช้ast.Raiseโหนดว่างอีกครั้งยกข้อยกเว้นที่ถูกจับ
การใช้ดีบักเกอร์เพื่อข้ามคำสั่งยืนยัน
งูหลามดีบักจริงช่วยให้คุณสามารถข้ามงบโดยใช้j/jumpคำสั่ง หากคุณรู้ล่วงหน้าว่าการยืนยันเฉพาะจะล้มเหลวคุณสามารถใช้วิธีนี้เพื่อข้ามมันได้ คุณสามารถเรียกใช้การทดสอบของคุณด้วย--traceซึ่งจะเปิดตัวดีบั๊กในตอนเริ่มต้นของการทดสอบทุกครั้งจากนั้นออก a j <line after assert>เพื่อข้ามเมื่อดีบักหยุดชั่วคราวก่อนที่จะยืนยัน
คุณสามารถทำได้โดยอัตโนมัติ การใช้เทคนิคด้านบนคุณสามารถสร้างปลั๊กอินดีบักเกอร์แบบกำหนดเองได้
- ใช้
pytest_testrun_call()hook เพื่อตรวจจับAssertionErrorข้อยกเว้น
- แยกหมายเลขบรรทัด 'offending' ของบรรทัดออกจากการย้อนกลับและอาจมีการวิเคราะห์ซอร์สโค้ดกำหนดหมายเลขบรรทัดก่อนและหลังการยืนยันที่จำเป็นในการดำเนินการกระโดดที่ประสบความสำเร็จ
- รันการทดสอบอีกครั้งแต่คราวนี้ใช้
Pdbคลาสย่อยที่กำหนดเบรกพอยต์บนบรรทัดก่อนที่จะยืนยันและดำเนินการกระโดดโดยอัตโนมัติไปที่สองเมื่อตีเบรกพอยต์ตามด้วยการcดำเนินการต่อ
หรือแทนที่จะรอให้การยืนยันล้มเหลวคุณสามารถตั้งค่าเบรกพอยต์ให้โดยอัตโนมัติสำหรับแต่ละรายการที่assertพบในการทดสอบ (อีกครั้งโดยใช้การวิเคราะห์ซอร์สโค้ดคุณสามารถแยกหมายเลขบรรทัดสำหรับast.Assertโหนดใน AST ของการทดสอบเล็กน้อย) ดำเนินการทดสอบที่ถูกยืนยัน ใช้คำสั่งสคริปต์ดีบักเกอร์และใช้jumpคำสั่งเพื่อข้ามการยืนยันตัวเอง คุณจะต้องทำการแลกเปลี่ยน; เรียกใช้การทดสอบทั้งหมดภายใต้ดีบักเกอร์ (ซึ่งช้าเพราะล่ามจะต้องเรียกใช้ฟังก์ชันการติดตามสำหรับทุกคำสั่ง) หรือใช้สิ่งนี้กับการทดสอบที่ล้มเหลวและจ่ายราคาของการรันการทดสอบเหล่านั้นอีกครั้งตั้งแต่เริ่มต้น
ปลั๊กอินดังกล่าวจะเป็นมากในการทำงานเพื่อสร้างฉันไม่ได้จะเขียนตัวอย่างที่นี่ส่วนหนึ่งเป็นเพราะมันจะไม่พอดีกับคำตอบอยู่แล้วและส่วนหนึ่งเป็นเพราะผมไม่คิดว่ามันคุ้มค่าเวลา ฉันเพิ่งจะเปิดตัวดีบักและทำการกระโดดด้วยตนเอง การยืนยันที่ล้มเหลวเป็นการระบุข้อผิดพลาดในการทดสอบตัวเองหรือการทดสอบภายใต้รหัสดังนั้นคุณอาจเพียงแค่มุ่งเน้นไปที่การดีบักปัญหา