পাইথনে পাইটেষ্টের দৃsert় বক্তব্য আচরণটি পরিবর্তন করা কি সম্ভব?


18

প্রকৃত এবং প্রত্যাশিত আচরণের সাথে মেলে আমি পাইথন দৃsert় বিবৃতিগুলি ব্যবহার করছি। এগুলির উপর আমার কোনও নিয়ন্ত্রণ নেই যেন কোনও ত্রুটি পরীক্ষার মামলা বাতিল হয়ে যায়। আমি দৃ error়তা ত্রুটির নিয়ন্ত্রণ নিতে চাই এবং আমি ব্যর্থতার প্রতিশ্রুতিতে টেস্টকেসটি বাতিল করতে চাই কিনা তা নির্ধারণ করতে চাই।

এছাড়াও আমি এমন কিছু যুক্ত করতে চাই যে যদি দৃ as়তা ত্রুটি থাকে তবে পরীক্ষার কেস থামিয়ে দেওয়া উচিত এবং ব্যবহারকারী যে কোনও মুহুর্তে পুনরায় শুরু করতে পারেন।

এটি কীভাবে করবেন তা সম্পর্কে আমার কোনও ধারণা নেই

কোড উদাহরণস্বরূপ, আমরা পাইটেষ্ট এখানে ব্যবহার করছি

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

Below is my expectation

যখন দৃsert়পদটি একটি দৃE়তা ত্রুটি নিক্ষেপ করে, তখন আমার কাছে টেস্টকেসটি থামিয়ে দেওয়ার একটি বিকল্প থাকা উচিত এবং এটি ডিবাগ এবং পরে পুনরায় শুরু করতে পারে। বিরতি এবং পুনরারম্ভের জন্য আমি tkinterমডিউলটি ব্যবহার করব । আমি নীচে হিসাবে একটি দৃsert় ফাংশন করব

import tkinter
import tkinter.messagebox

top = tkinter.Tk()

def _assertCustom(assert_statement, pause_on_fail = 0):
    #assert_statement will be something like: assert a == 10, "Some error"
    #pause_on_fail will be derived from global file where I can change it on runtime
    if pause_on_fail == 1:
        try:
            eval(assert_statement)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            eval (assert_statement)
            #Above is to raise the assertion error again to fail the testcase
    else:
        eval (assert_statement)

এগিয়ে যেতে আমাকে এই ফাংশন সহ প্রতিটি প্রতিবেদন বিবৃতি পরিবর্তন করতে হবে

import pytest
def test_abc():
    a = 10
    # Suppose some code and below is the assert statement 
    _assertCustom("assert a == 10, 'error message'")

এটি আমার পক্ষে অনেক বেশি প্রচেষ্টা যেহেতু আমি যে হাজার হাজার জায়গাগুলি আমি দৃsert়ভাবে উল্লেখ করেছি সেখানে পরিবর্তন করতে হবে। এটি করার কোনও সহজ উপায় আছে কি?pytest

Summary:আমার এমন কিছু দরকার যেখানে আমি টেস্টকেসটি ব্যর্থতার পরে থামাতে পারি এবং তারপরে ডিবাগিংয়ের পরে আবার শুরু করতে পারি। আমি জানি tkinterএবং সে কারণেই আমি এটি ব্যবহার করেছি। অন্য কোনও ধারণা স্বাগত জানানো হবে

Note: উপরে কোড এখনও পরীক্ষা করা হয় না। ছোট ছোট সিনট্যাক্স ত্রুটিও থাকতে পারে

সম্পাদনা: উত্তরের জন্য ধন্যবাদ। এই প্রশ্নটি এখন একটু সামনে বাড়ানো। আমি যদি দৃsert় আচরণের পরিবর্তন করতে চাই What বর্তমানে যখন একটি দৃ error়তা ত্রুটি টেস্টকেস থেকে বেরিয়ে আসে। আমার যদি নির্দিষ্ট টেস্টকেস ব্যর্থতা বা না হওয়াতে টেস্টকেস প্রস্থান প্রয়োজন হয় তবে আমি যদি চয়ন করতে চাই। আমি উপরে উল্লিখিত হিসাবে কাস্টম assert ফাংশন লিখতে চাই না কারণ এইভাবে আমাকে জায়গাগুলির সংখ্যাতে পরিবর্তন করতে হবে


3
আপনি আমাদের যা করতে চান তার একটি কোড উদাহরণ দিতে পারেন?
mrblewog

1
ব্যবহার না করে assertআপনার নিজের চেকিং ফাংশনগুলি লিখুন যা আপনি চান তা করেন।
molbdnilo

কেন আপনি সন্নিবেশ আত জাহির মধ্যে ব্যবহার করে দেখুন ব্লক এবং ত্রুটি বার্তা ছাড়া ?
প্রতীক কিনি

1
মনে হচ্ছে আপনি যা চান তা pytestআপনার পরীক্ষার ক্ষেত্রে ব্যবহার করা like এটি টেস্ট স্যুট রাইটিংকে আরও সহজ করে তোলে এমন আরও অনেকগুলি বৈশিষ্ট্যের পাশাপাশি দৃsert ়তা এবং স্কিপিং টেস্টগুলি সমর্থন করে supports
blubberdiblub

1
এমন কোনও সাধারণ সরঞ্জামটি লিখতে কি খুব সোজা হবে না যা যান্ত্রিকভাবে assert cond, "msg"আপনার কোডের প্রত্যেকটির সাথে প্রতিস্থাপন করবে _assertCustom("assert cond, 'msg'")? সম্ভবত একটি sedওয়ান-লাইনার এটি করতে পারে।
এনপিই

উত্তর:


23

আপনি ব্যবহার করছেন pytest, যা আপনাকে ব্যর্থ পরীক্ষার সাথে ইন্টারঅ্যাক্ট করার জন্য পর্যাপ্ত বিকল্প দেয় gives এটি আপনাকে কমান্ড লাইন বিকল্পগুলি এবং এটি সম্ভব করার জন্য কয়েকটি হুক দেয়। আমি কীভাবে প্রতিটি ব্যবহার করব এবং আপনার নির্দিষ্ট ডিবাগিংয়ের প্রয়োজনীয়তার সাথে আপনি কাস্টমাইজেশন তৈরি করতে পারবেন তা আমি ব্যাখ্যা করব।

আমি আরও বেশি বিদেশী বিকল্পগুলির মধ্যে যাব যা আপনাকে নির্দিষ্ট দৃ as়তা সম্পূর্ণরূপে এড়িয়ে যাওয়ার অনুমতি দেয়, যদি আপনি সত্যিই মনে করেন যে আপনার অবশ্যই আবশ্যক।

জোর দেওয়া নয়, ব্যতিক্রমগুলি পরিচালনা করুন

নোট করুন যে একটি ব্যর্থ পরীক্ষা সাধারণত পাইস্টেস্ট বন্ধ করে না; কেবলমাত্র যদি আপনি স্পষ্টভাবে নির্দিষ্ট নম্বর ব্যর্থতার পরে প্রস্থান করতে স্পষ্টভাবে বলতে সক্ষম হন । এছাড়াও, পরীক্ষা ব্যর্থ হয় কারণ একটি ব্যতিক্রম উত্থাপিত হয়; assertউত্থাপন করে AssertionErrorতবে এটিই কেবল ব্যতিক্রম নয় যা পরীক্ষায় ব্যর্থ হতে পারে! আপনি কীভাবে ব্যতিক্রমগুলি পরিচালনা করা হয় তা নিয়ন্ত্রণ করতে চান, পরিবর্তিত হয় না assert

তবে, একটি ব্যর্থতা দৃ as় পৃথক পরীক্ষা শেষ হবে । এটি কারণ যে একবার try...exceptব্লকের বাইরে কোনও ব্যতিক্রম উত্থাপিত হলে পাইথন বর্তমান ফাংশন ফ্রেমটি উন্মুক্ত করে দেয় এবং এটির পিছনে আর ফিরে আসে না।

আমি মনে করি না যে এটি আপনি কী চান, আপনার _assertCustom()দাবিটি পুনরায় চালনার আপনার বিবরণ দিয়ে বিচার করে তবে আমি আপনার বিকল্পগুলি আরও পরে আলোচনা করব।

পিডিবির সাথে পাইস্টে পোস্ট-মর্টেম ডিবাগিং

একটি ডিবাজারে ব্যর্থতাগুলি পরিচালনা করতে বিভিন্ন বিকল্পের জন্য, আমি --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
[ ... ]

এই স্যুইচটি দিয়ে, যখন কোনও পরীক্ষা ব্যর্থ হয় তখন পাইস্টেস্ট পোস্ট-মর্টেম ডিবাগিং সেশন শুরু করে । এটি মূলত আপনি যা চেয়েছিলেন ঠিক তেমনই; একটি ব্যর্থ পরীক্ষার সময়ে কোডটি থামাতে এবং আপনার পরীক্ষার অবস্থার দিকে নজর দেওয়ার জন্য ডিবাগারটি খুলুন। আপনি পরীক্ষার স্থানীয় ভেরিয়েবলগুলি, গ্লোবালগুলি এবং স্ট্যাকের প্রতিটি ফ্রেমের স্থানীয় এবং গ্লোবালগুলির সাথে যোগাযোগ করতে পারেন।

এখানে পাইস্টেস্ট আপনাকে এই বিন্দুটির পরে প্রস্থান করা হবে কি না তার পুরো নিয়ন্ত্রণ দেয়: আপনি যদি qপ্রস্থান কমান্ডটি ব্যবহার করেন তবে পাইটেষ্ট রানটিও প্রস্থান করে, cচালিয়ে যাওয়ার জন্য ব্যবহার করে পাইটেষ্টের নিয়ন্ত্রণ ফিরে আসবে এবং পরবর্তী পরীক্ষাটি কার্যকর করা হবে।

বিকল্প ডিবাগার ব্যবহার করা

আপনি pdbএটির জন্য ডিবাগারে আবদ্ধ নন ; আপনি --pdbclsস্যুইচ দিয়ে একটি ভিন্ন ডিবাগার সেট করতে পারেন। কোন pdb.Pdb()সামঞ্জস্যপূর্ণ বাস্তবায়ন কাজ করবে সহ IPython ডিবাগার বাস্তবায়ন , বা অন্যান্য অধিকাংশ পাইথন debuggers ( pudb ডিবাগার প্রয়োজন -sসুইচ ব্যবহার করা হয়, অথবা একটি বিশেষ প্লাগইন )। স্যুইচটি একটি মডিউল এবং বর্গ নেয়, উদাহরণস্বরূপ আপনি ব্যবহার pudbকরতে পারেন এমন ব্যবহার করতে:

$ pytest -s --pdb --pdbcls=pudb.debugger:Debugger

আপনি এই বৈশিষ্ট্যটি চারপাশে আপনার নিজের র‍্যাপার ক্লাস লিখতে ব্যবহার করতে Pdbপারেন যা নির্দিষ্ট ব্যর্থতা আপনার আগ্রহী কিছু না হলে কেবল তাত্ক্ষণিকভাবে ফিরে আসে like ঠিক যেমনটি pytestব্যবহার করে :Pdb()pdb.post_mortem()

p = Pdb()
p.reset()
p.interaction(None, t)

এখানে, tএকটি ট্রেসব্যাক অবজেক্ট । যখন p.interaction(None, t)ফিরে আসে, pytestপরবর্তী পরীক্ষা দিয়ে চালিয়ে যান, যদি না p.quitting সেট করা থাকেTrue (যারপরে পাইস্টেস্টটি প্রস্থান করে)।

এখানে একটি উদাহরণ বাস্তবায়ন যা মুদ্রণ করে যে আমরা ডিবাগ করতে অস্বীকার করছি এবং অবিলম্বে ফিরে আসবে, যদি না পরীক্ষাটি উত্থাপিত না হয় তবে 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 বা অনুরূপ কিছু ব্যবহার করে নিজের ডিবাগারটি লিখতে না চাইলে আমি সত্যিই এই বিকল্পটির সুপারিশ করতে পারি না। নোট করুন যে এটি একটি বড় উদ্যোগ।

ফিল্টারিং ব্যর্থতা; ডিবাগারটি কখন খুলবেন তা চয়ন করুন

পরবর্তী স্তরটি হ'ল পাইস্ট ডিবাগিং এবং ইন্টারঅ্যাকশন হুক ; এগুলি হাইড পয়েন্টগুলি হ'ল আচরণ কাস্টমাইজেশনের জন্য, পাইটেষ্ট কীভাবে সাধারণত কোনও ব্যতিক্রম পরিচালনা করে বা ডিবাগারে প্রবেশ করে pdb.set_trace()বা breakpoint()(পাইথন ৩.7 বা আরও নতুন) এর মতো জিনিসগুলি পরিচালনা করে তা বাড়ানোর জন্য ।

এই হুকটির অভ্যন্তরীণ বাস্তবায়ন >>> entering PDB >>>উপরের ব্যানারটিও প্রিন্ট করার জন্য দায়ী , সুতরাং ডিবাগারটি চালানো থেকে রোধ করতে এই হুকটি ব্যবহার করার অর্থ আপনি এই আউটপুটটি মোটেও দেখতে পাবেন না। পরীক্ষার ব্যর্থতা যখন 'আকর্ষণীয়' হয় তখন আপনার নিজের হুকটি মূল হুকটিতে ডেলিগেট করতে পারে এবং সুতরাং আপনি যে ডিবাগারটি ব্যবহার করছেন তার থেকে পৃথক ফিল্টার পরীক্ষার ব্যর্থতা ! আপনি নাম দ্বারা এটি অ্যাক্সেস করে অভ্যন্তরীণ বাস্তবায়ন অ্যাক্সেস করতে পারেন ; এর জন্য অভ্যন্তরীণ হুক প্লাগইনটির নাম দেওয়া হয়েছে pdbinvoke। এটি চালানো থেকে রোধ করতে আপনার এটিকে নিবন্ধভুক্ত করতে হবে তবে একটি রেফারেন্স সংরক্ষণ করুন আমরা কীভাবে এটি প্রয়োজন হিসাবে সরাসরি কল করতে পারি।

এখানে যেমন একটি হুক একটি নমুনা বাস্তবায়ন; প্লাগিনগুলি লোড হওয়া যে কোনও অবস্থানের মধ্যে আপনি এটি রাখতে পারেন ; আমি এটি ভিতরে রাখা 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_interactঅন্য হুকের মাধ্যমে হুকের সাহায্যে প্লাগইন অবজেক্টটিকে নিবন্ধভুক্ত করে 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 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-pudbবা pytest-pycharm) তাদের নিজস্ব pytest_exception_interactহুকস্প্প নিবন্ধন করে । প্রতিটি সম্পূর্ণ প্লাগইন ব্যবহার করে config.pluginmanager.list_name_pluginএবং hasattr()পরীক্ষার জন্য স্বেচ্ছাসেবী প্লাগইনগুলি ওভাররাইড করতে প্লাগইন-ম্যানেজারের সমস্ত প্লাগইনকে আরও সম্পূর্ণ বাস্তবায়ন করতে হবে over

ব্যর্থতা পুরোপুরি চলে যায়

এটি আপনাকে ব্যর্থ টেস্ট ডিবাগিংয়ের উপরে পুরোপুরি নিয়ন্ত্রণ প্রদান করার পরে, যদি আপনি প্রদত্ত পরীক্ষার জন্য ডিবাগারটি না খোলার জন্য পছন্দ না করেও এটি পরীক্ষাকে ব্যর্থ হিসাবে ফেলে দেয় । আপনি ব্যর্থতা পুরাপুরি দূরে যেতে করতে চান, আপনি ব্যবহার একটি আলাদা হুক করতে পারেন: pytest_runtest_call()

পাইস্টেস্ট পরীক্ষা চালালে, এটি উপরের হুকের মাধ্যমে পরীক্ষা চালাবে, যা প্রত্যাশিত Noneবা ব্যতিক্রম বাড়াতে পারে । এটি থেকে একটি প্রতিবেদন তৈরি করা হয়, allyচ্ছিকভাবে একটি লগ এন্ট্রি তৈরি করা হয়, এবং যদি পরীক্ষাটি ব্যর্থ হয়, তবে পূর্বোক্ত pytest_exception_interact()হুক বলা হবে। সুতরাং আপনাকে যা করতে হবে তা হুক এর ফলশ্রুতিতে ফলাফলটি পরিবর্তন করে; ব্যতিক্রম পরিবর্তে এটি কিছুতেই ফিরে আসা উচিত নয়।

এটি করার সর্বোত্তম উপায় হুক র্যাপার ব্যবহার করা । হুক মোড়কের আসল কাজটি করতে হবে না, তবে পরিবর্তে হুকের ফলাফলের সাথে কী ঘটে তা পরিবর্তনের সুযোগ দেওয়া হয়। আপনাকে যা করতে হবে তা হ'ল লাইনটি যুক্ত করুন:

outcome = yield

আপনার হুক মোড়ক বাস্তবায়নে এবং আপনি মাধ্যমে পরীক্ষার ব্যতিক্রম সহ হুক ফলাফলের অ্যাক্সেস পাবেন outcome.excinfo। পরীক্ষায় কোনও ব্যতিক্রম উত্থাপিত হলে এই বৈশিষ্ট্যটি (টাইপ, উদাহরণ, ট্রেসব্যাক) এর একটি টিপলকে সেট করা হয়। বিকল্পভাবে, আপনি কল করতে পারেন outcome.get_result()এবং স্ট্যান্ডার্ড try...exceptহ্যান্ডলিং ব্যবহার করতে পারেন ।

তাহলে আপনি কীভাবে ব্যর্থ পরীক্ষার পাস করবেন? আপনার কাছে 3 টি বেসিক বিকল্প রয়েছে:

  • আপনি মোড়কে কল করে পরীক্ষাকে প্রত্যাশিত ব্যর্থতা হিসাবে চিহ্নিত করতে পারেন pytest.xfail()
  • আপনি আইটেমটি এড়িয়ে গেছেন হিসাবে চিহ্নিত করতে পারেন , যা ভান করে যে কলটি করার মাধ্যমে পরীক্ষাটি প্রথম স্থানে কখনও চালানো হয়নি pytest.skip()
  • আপনি outcome.force_result()পদ্ধতিটি ব্যবহার করে ব্যতিক্রমটি সরিয়ে ফেলতে পারেন ; ফলাফলটি এখানে একটি খালি তালিকায় সেট করুন (যার অর্থ: নিবন্ধিত হুক ছাড়া আর কিছুই তৈরি হয়নি None) এবং ব্যতিক্রমটি পুরোপুরি সাফ হয়ে যায়।

আপনি যা ব্যবহার করেন তা আপনার উপর নির্ভর করে। প্রথমে এড়িয়ে যাওয়া এবং প্রত্যাশিত-ব্যর্থতা পরীক্ষার জন্য ফলাফলটি পরীক্ষা করে নেওয়ার বিষয়টি নিশ্চিত করে নিন যেহেতু আপনাকে সেইগুলি কেসগুলি হ্যান্ডেল করার দরকার নেই যেমন পরীক্ষাটি ব্যর্থ হয়েছে। এই বিকল্পগুলি pytest.skip.Exceptionএবং এর মাধ্যমে উত্থাপিত বিশেষ ব্যতিক্রমগুলি আপনি অ্যাক্সেস করতে পারেন pytest.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]) তবে আপনি চান অন্য কোনও বার্তা ফর্ম্যাটটি ব্যবহার করতে পারবেন না।

তিনটি ক্ষেত্রেই পাইস্টেস্ট পরীক্ষার জন্য ডিবাগারটি খুলবে না যার ফলাফল আপনি এই পদ্ধতিটি ব্যবহার করে পরিবর্তন করেছেন।

পৃথক দাবী বিবৃতি পরিবর্তন

আপনি যদি কোনওassert পরীক্ষার মধ্যে পরীক্ষাগুলি পরিবর্তন করতে চান তবে আপনি আরও অনেক বেশি কাজের জন্য নিজেকে সেট আপ করছেন। হ্যাঁ, এটি প্রযুক্তিগতভাবে সম্ভব, তবে পাইথন সংকলনের সময় যে কোডটি কার্যকর করতে চলেছে কেবল তা আবার লিখে দিয়ে

আপনি যখন ব্যবহার করবেন pytest, এটি ইতিমধ্যে ইতিমধ্যে করা হচ্ছে । Pytest নতুন করে লেখা হয় assertআপনি আরো কনটেক্সট দিতে যখন আপনার দাবি ব্যর্থ বিবৃতি ; ঠিক কী করা হচ্ছে তার পাশাপাশি উত্স কোডের একটি ভাল ওভারভিউয়ের জন্য এই ব্লগ পোস্টটি দেখুন । নোট করুন যে মডিউলটি 1k লাইনের বেশি লম্বা, এবং পাইথনের বিমূর্ত সিনট্যাক্স ট্রি কীভাবে কাজ করে তা আপনার বুঝতে হবে । যদি আপনি এটি করেন তবে আপনি কোনও হ্যান্ডলারের সাথে পার্শ্ববর্তীসহ নিজের নিজস্ব পরিবর্তনগুলি যুক্ত করতে সেখানে সেই মডিউলটি বানরপ্যাচ করতে পারেন ।_pytest/assertion/rewrite.pyasserttry...except AssertionError:

তবে , আপনি কেবলমাত্র নির্বাচনগুলি যথাযথভাবে অক্ষম বা অগ্রাহ্য করতে পারবেন না, কারণ পরবর্তী বিবৃতিগুলি সহজেই রাষ্ট্রের (নির্দিষ্ট অবজেক্টের বিন্যাস, ভেরিয়েবল সেট ইত্যাদি) উপর নির্ভর করতে পারে যা একটি এড়িয়ে যাওয়া দাবির বিরুদ্ধে রক্ষা করা ছিল। যদি কোনও জোর পরীক্ষা fooনা হয় Noneতবে পরে যুক্তিটি foo.barঅস্তিত্বের উপর নির্ভর করে , তবে আপনি কেবল AttributeErrorসেখানে চলে যাবেন ইত্যাদি the ব্যতিক্রমটিকে পুনরায় উত্থাপন করতে আটকে থাকুন, যদি আপনাকে এই পথে যেতে হয় তবে need

আমি assertsএখানে পুনরায় লেখার বিষয়ে আরও বিশদে যেতে যাচ্ছি না , কারণ আমি মনে করি না যে এটি অনুসরণ করা উপযুক্ত, এতে জড়িত কাজের পরিমাণ দেওয়া হয়নি এবং পোস্ট-মর্টেম ডিবাগিংয়ের সাথে পরীক্ষার রাজ্যে আপনাকে অ্যাক্সেস প্রদান করবে দৃ failure়তা ব্যর্থতা যাইহোক বিন্দু ।

নোট করুন যে আপনি যদি এটি করতে চান তবে আপনার ব্যবহারের দরকার নেই eval()(যা কোনওভাবেই কাজ করবে না, assertএটি একটি বিবৃতি, সুতরাং এর exec()পরিবর্তে আপনার ব্যবহারের দরকার হবে), অথবা আপনাকে দু'বার দাবি চালাতে হবে না (যা কোনটি নয়) দৃ the়তা পরিবর্তিত অবস্থায় যদি অভিব্যক্তিটি ব্যবহৃত হয় তবে বিষয়টি নিয়ে যেতে পারে। আপনি পরিবর্তে ast.Assertনোডের ভিতরে নোড এম্বেড করবেন ast.Tryএবং একটি ফাঁকা ast.Raiseনোড ব্যবহার করে এমন একটি হ্যান্ডলার সংযুক্ত করবেন যা ধরা পড়েছিল এমন ব্যতিক্রম পুনরায় উত্থাপন করবে।

দৃser় বিবৃতি এড়ানোর জন্য ডিবাগার ব্যবহার করে।

পাইথন ডিবাগার আসলে / কমান্ড ব্যবহার করে আপনাকে বিবৃতি এড়িয়ে যেতে দেয় । আপনি কি জানেন সামনে যা নির্দিষ্ট কোনো বিবৃতি হবে ব্যর্থ, আপনি এটি বাইপাস এই ব্যবহার করতে পারেন। আপনি আপনার পরীক্ষাগুলি দিয়ে চালাতে পারেন , যা প্রতিটি পরীক্ষার শুরুতে ডিবাগারটি খোলায় , তারপরে ডিবাগারটি যখন দাবী করার ঠিক আগে থামিয়ে দেওয়া হয় তখন এটিকে এড়িয়ে যেতে একটি ইস্যু করুন ।jjump--tracej <line after assert>

আপনি এটি স্বয়ংক্রিয় করতে পারেন। উপরের কৌশলগুলি ব্যবহার করে আপনি একটি কাস্টম ডিবাগার প্লাগইন তৈরি করতে পারেন

  • ব্যতিক্রম pytest_testrun_call()ধরতে হুক ব্যবহার করেAssertionError
  • ট্রেসব্যাকটি থেকে 'আপত্তিকর' লাইন নম্বরটি বের করে এবং সম্ভবত কিছু উত্স কোড বিশ্লেষণের মাধ্যমে একটি সফল জাম্প চালানোর জন্য প্রয়োজনীয় দৃ the়তার আগে এবং পরে লাইন নম্বরগুলি নির্ধারণ করে
  • আবার পরীক্ষা চালায় , তবে এবার একটি Pdbসাবক্লাস ব্যবহার করে যা দৃsert়তার আগে লাইনে একটি ব্রেকপয়েন্ট নির্ধারণ করে এবং ব্রেকপয়েন্টটি আঘাত হানার পরে স্বয়ংক্রিয়ভাবে দ্বিতীয়টিতে একটি লাফ চালায় এবং তারপরে একটি cচালিয়ে যেতে হবে।

অথবা, ব্যর্থ হওয়ার জন্য একটি দৃ to়তার জন্য অপেক্ষা করার পরিবর্তে, আপনি assertপরীক্ষায় প্রাপ্ত প্রতিটিটির জন্য ব্রেকিংপয়েন্টগুলি সেট করে স্বয়ংক্রিয় করতে পারেন (আবার উত্স কোড বিশ্লেষণ ব্যবহার করে, আপনি ast.Assertপরীক্ষার একটি এএসটিতে নোডের জন্য তুচ্ছভাবে লাইন নম্বরগুলি বের করতে পারেন ), দৃ test়তম পরীক্ষার সম্পাদন করুন ডিবাগার স্ক্রিপ্টযুক্ত কমান্ডগুলি ব্যবহার করে, এবং jumpদৃ itself়তা বাদ দিতে কমান্ডটি ব্যবহার করুন । আপনাকে একটি ট্রেড অফ করতে হবে; একটি ডিবাগারের অধীনে সমস্ত পরীক্ষা চালান (যা দোভাষী প্রত্যেক বিবৃতিতে একটি ট্রেস ফাংশন কল করতে হয় তাই ধীর হয়) বা কেবল ব্যর্থ পরীক্ষায় এটি প্রয়োগ করুন এবং স্ক্র্যাচ থেকে সেই পরীক্ষাগুলি পুনরায় চালনার জন্য মূল্য প্রদান করুন pay

এই ধরনের একটি প্লাগইন তৈরি করতে অনেক কাজ হবে, আমি এখানে একটি উদাহরণ লিখব না, আংশিক কারণ এটি কোনওভাবেই কোনও জবাবের সাথে খাপ খায় না, এবং আংশিক কারণ এটি আমার পক্ষে সময়টির পক্ষে উপযুক্ত মনে হয় না । আমি সবেমাত্র ডিবাগারটি খুলি এবং নিজেই লাফিয়ে উঠতাম। একটি ব্যর্থতার জোড় পরীক্ষা নিজেই বা কোড-আন্ডার-টেস্টের মধ্যে একটি বাগ নির্দেশ করে, যাতে আপনি কেবল সমস্যাটি ডিবাগিংয়ে ফোকাস করতে পারেন।


7

পাইস্ট - পিডিবি দিয়ে কোনও কোড পরিবর্তন ছাড়াই আপনি যা চান তা অর্জন করতে পারেন ।

আপনার উদাহরণ সহ:

import pytest
def test_abc():
    a = 9
    assert a == 10, "some error message"

--Pdb দিয়ে চালান:

py.test --pdb
collected 1 item

test_abc.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_abc():
        a = 9
>       assert a == 10, "some error message"
E       AssertionError: some error message
E       assert 9 == 10

test_abc.py:4: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /private/tmp/a/test_abc.py(4)test_abc()
-> assert a == 10, "some error message"
(Pdb) p a
9
(Pdb)

পরীক্ষা ব্যর্থ হওয়ার সাথে সাথে আপনি বিল্টিন পাইথন ডিবাগার দিয়ে এটি ডিবাগ করতে পারেন। আপনি যদি ডিবাগিং সম্পন্ন করেন, আপনি continueবাকী পরীক্ষা দিয়ে করতে পারেন ।


যখন পরীক্ষার কেস ব্যর্থ হয় বা পরীক্ষার পদক্ষেপ ব্যর্থ হয় তখনই কি এটি বন্ধ হবে।
নীতেশ

দয়া করে লিঙ্কযুক্ত ডক্সটি
gnvk

চমৎকার ধারণা. তবে --pdb ব্যবহার করা হলে টেস্টকেস প্রতিটি ব্যর্থতায় বিরতি দেয়। রান-টাইমে আমি কী সিদ্ধান্ত নিতে পারি যে ব্যর্থতাতে আমি পরীক্ষার
কেসটি

5

আপনি যদি পাইচার্ম ব্যবহার করছেন তবে যখনই কোনও দাবি ব্যর্থ হয় আপনি এক্সিকিউশন বিরতিতে একটি ব্যতিক্রম ব্রেকপয়েন্ট যোগ করতে পারেন। ব্রেকপয়েন্টগুলি দেখুন (সিটিআরএল-শিফট-এফ 8) এবং এসেরেশনআরারের জন্য একটি অন-উত্সাহ ব্যতিক্রম হ্যান্ডলার যুক্ত করুন। দ্রষ্টব্য যে এটি পরীক্ষাগুলি কার্যকর করতে ধীর করতে পারে।

অন্যথায়, আপনি যদি প্রতিটি ব্যর্থ পরীক্ষার শেষে (যদি ত্রুটিগুলির ঠিক আগে) বিন্দুতে না দাঁড়ানোর পরিবর্তে বিরক্তিটি মনে করেন না, তবে আপনার কাছে কয়েকটি বিকল্প রয়েছে। তবে নোট করুন যে এই মুহুর্তে বিভিন্ন ক্লিনআপ কোড, যেমন পরীক্ষায় খোলা ফাইলগুলি ক্লোজ করা ইতিমধ্যে চালানো হয়েছিল। সম্ভাব্য বিকল্পগুলি হ'ল:

  1. আপনি পাইস্টকে বলতে পারেন --pdb বিকল্পটি ব্যবহার করে ত্রুটিপূর্ণ অবস্থায় আপনাকে ডিবাগারে ফেলে দিতে ।

  2. আপনি নীচের সাজসজ্জারকে সংজ্ঞায়িত করতে এবং এর সাথে প্রতিটি প্রাসঙ্গিক পরীক্ষা ফাংশনটি সাজাতে পারেন। (এছাড়াও একটি বার্তা লগিং ইন, আপনি একটি শুরু করতে পারে pdb.post_mortem এই সময়ে, অথবা এমনকি একটি ইন্টারেক্টিভ code.interact ফ্রেম যেখানে ব্যতিক্রম সম্ভূত, বর্ণনা অনুযায়ী এর স্থানীয়দের সঙ্গে এই উত্তর ।)

from functools import wraps

def pause_on_assert(test_func):
    @wraps(test_func)
    def test_wrapper(*args, **kwargs):
        try:
            test_func(*args, **kwargs)
        except AssertionError as e:
            tkinter.messagebox.showinfo(e)
            # re-raise exception to make the test fail
            raise
    return test_wrapper

@pause_on_assert
def test_abc()
    a = 10
    assert a == 2, "some error message"
  1. আপনি নিজে যে পরীক্ষা ফাংশন সাজাইয়া রাখা চাই না থাকে, তাহলে আপনি পরিবর্তে একটি autouse চোকান যে পরিদর্শনে বর্ণনা করতে পারেন sys.last_value :
import sys

@pytest.fixture(scope="function", autouse=True)
def pause_on_assert():
    yield
    if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
        tkinter.messagebox.showinfo(sys.last_value)

উত্তরটি আমি সাজসজ্জার সাথে পছন্দ করেছিলাম তবে এটি গতিশীলভাবে করা যায় না। যখন আমি বিরতি_অন_সেসার্ট করতে চাই বা না চাই তখন আমি গতিশীলভাবে নিয়ন্ত্রণ করতে চাই। এটির জন্য কি কোন সমাধান আছে?
নীতেশ

কীভাবে গতিশীল? যেমন একক স্যুইচ হিসাবে এটি সক্ষম / অক্ষম সর্বত্র? বা প্রতিটি পরীক্ষার জন্য এটি নিয়ন্ত্রণ করার কোনও উপায়?
উরি গ্রান্ট

ধরুন আমি কিছু টেস্টকেস চালাচ্ছি। মাঝখানে আমি ব্যর্থতার বিরতিতে প্রয়োজনীয়তা পেয়েছি। আমি স্যুইচ সক্ষম করব। পরে যেকোন সময় আমি অনুভব করি যে আমার সুইচটি অক্ষম করা দরকার।
নীতেশ

উত্তরে আপনার ডেকোরেটর: 2 আমার পক্ষে কাজ করবে না কারণ আমার পরীক্ষার কেস একাধিক দাবি করেছে
নীতেশ

একটি 'স্যুইচ' সম্পর্কিত, আপনি pause_on_assertবিরতি দেবেন কি না তা স্থির করার জন্য কোনও ফাইল থেকে পড়ার বাস্তবায়ন আপডেট করতে পারেন ।
উরি গ্রান্ট

4

একটি সহজ সমাধান, আপনি যদি ভিজ্যুয়াল স্টুডিও কোডটি ব্যবহার করতে ইচ্ছুক হন তবে শর্তযুক্ত ব্রেকপয়েন্টগুলি ব্যবহার করা যেতে পারে ।

এটি আপনাকে আপনার দাবিগুলি সেট আপ করার অনুমতি দেবে, উদাহরণস্বরূপ:

import pytest
def test_abc():
    a = 10
    assert a == 10, "some error message"

তারপরে আপনার দৃsert় রেখায় শর্তযুক্ত ব্রেকপয়েন্টটি যুক্ত করুন যা কেবলমাত্র যখন আপনার দৃ when়তা ব্যর্থ হবে:

এখানে চিত্র বর্ণনা লিখুন


@ নীতেশ - আমি মনে করি এই সমাধানটি আপনার সমস্ত সমস্যার সমাধান করে, আপনি কেবল তখনই বিরতি দেন যখন একটি দৃser়তা ব্যর্থ হয়, আপনি সেখানে কোডটি ডিবাগ করতে পারেন এবং তারপরে আপনি বাকি পরীক্ষাগুলি চালিয়ে যেতে পারেন ... তবে এটি সেট আপ করা একটু বেশি জটিল বলে মনে হচ্ছে প্রাথমিকভাবে
নিক মার্টিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.