अजगर unittest - मुखर के विपरीत?


374

मैं यह स्थापित करने के लिए एक परीक्षण लिखना चाहता हूं कि किसी अपवाद को किसी परिस्थिति में नहीं उठाया गया है।

यदि एक अपवाद उठाया जाता है तो यह परीक्षण करना सरल है ...

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

... लेकिन आप इसके विपरीत कैसे कर सकते हैं ।

कुछ इस तरह से मैं जो मैं के बाद हूँ ...

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath) 

6
आप हमेशा परीक्षण में काम करने के लिए जो कुछ भी करना चाहते थे, कर सकते हैं। यदि यह एक त्रुटि उठाता है, तो वह दिखाई देगा (एक विफलता के बजाय एक त्रुटि के रूप में गिना जाता है)। बेशक, यह मानता है कि यह किसी भी प्रकार की त्रुटि के बजाय किसी भी त्रुटि को नहीं बढ़ाता है। इसके अलावा, मुझे लगता है कि आपको अपना खुद का लिखना होगा।
थॉमस के


यह पता चला है कि आप वास्तव में एक assertNotRaisesविधि को लागू कर सकते हैं जो अपने कोड / व्यवहार का 90% कोड के assertRaisesबारे में ~ 30-ईश लाइनों के साथ साझा करता है। देखें नीचे मेरा उत्तर जानकारी के लिए।
tel

मैं यह चाहता हूं इसलिए मैं दो कार्यों की तुलना hypothesisयह सुनिश्चित करने के लिए कर सकता हूं कि वे सभी प्रकार के इनपुट के लिए एक ही आउटपुट का उत्पादन करते हैं, जबकि उन मामलों की अनदेखी करते हैं जहां मूल एक अपवाद उठाता है। assume(func(a))काम नहीं करता क्योंकि आउटपुट अस्पष्ट सत्य मान के साथ एक सरणी हो सकता है। इसलिए मैं सिर्फ एक फ़ंक्शन को कॉल करना चाहता हूं और प्राप्त करना चाहता हूं Trueयदि यह विफल नहीं होता है। assume(func(a) is not None)मुझे लगता है कि काम करता है
एंडोलिथ

जवाबों:


394
def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")

32
@hiwaylon - नहीं, यह वास्तव में सही समाधान है। उपयोगकर्ता 9876 द्वारा प्रस्तावित समाधान अवधारणात्मक रूप से त्रुटिपूर्ण है: यदि आप कहने के लिए गैर-वृद्धि के लिए परीक्षण करते हैं ValueError, लेकिन ValueErrorइसके बजाय उठाया जाता है, तो आपके परीक्षण को एक असफलता की स्थिति से बाहर निकलना चाहिए, न कि त्रुटि। दूसरी ओर, यदि उसी कोड को चलाने में आप एक जुट करेंगे KeyError, तो यह एक त्रुटि होगी, विफलता नहीं। अजगर में - कुछ अन्य भाषाओं की तुलना में भिन्न - अपवाद नियंत्रण प्रवाह के लिए नियमित रूप से उपयोग किए जाते हैं, यही कारण है कि हमारे पास except <ExceptionName>वास्तव में वाक्यविन्यास है। उस संबंध में, user9876 का समाधान केवल गलत है।
मैक

@ एमएसी - क्या यह भी एक सही समाधान है? stackoverflow.com/a/4711722/6648326
मास्टरजेओ 2

यह एक परीक्षण के लिए <100% कवरेज (अपवाद कभी नहीं होगा) दिखाने का दुर्भाग्यपूर्ण प्रभाव है।
Shay

3
@ सहाय, आईएमओ आपको हमेशा टेस्ट फाइलों को कवरेज रिपोर्ट से बाहर रखना चाहिए (जैसा कि वे लगभग हमेशा परिभाषा द्वारा 100% चलाते हैं, आप कृत्रिम रूप से रिपोर्ट को बढ़ा-चढ़ाकर पेश करेंगे)
मूल BBQ सॉस

@ मूल- bbq- सॉस, जो मुझे अनजाने में छोड़े गए परीक्षणों के लिए खुला नहीं छोड़ेगा। उदाहरण के लिए, टेस्ट नाम में टाइपो (ttst_function), pycharm में गलत रन कॉन्फ़िगरेशन, आदि?
Shay

67

हाय - मैं यह निर्धारित करने के लिए एक परीक्षण लिखना चाहता हूं कि किसी अपवाद को किसी परिस्थिति में नहीं उठाया गया है।

यह डिफ़ॉल्ट धारणा है - अपवाद नहीं उठाए गए हैं।

यदि आप और कुछ नहीं कहते हैं, तो यह हर एक परीक्षा में माना जाता है।

आपको वास्तव में इसके लिए कोई अभिकथन लिखने की जरूरत नहीं है।


7
@IndradhanushGupta अच्छी तरह से स्वीकार किए जाते हैं जवाब इस एक से अधिक pythonic परीक्षण करता है। निहितार्थ की तुलना में स्पष्ट है।
0xc0de

17
किसी अन्य टिप्पणीकार ने यह नहीं बताया कि यह उत्तर गलत क्यों है, हालांकि यह एक ही कारण है कि उपयोगकर्ता 9876 का उत्तर गलत है: असफलताएं और त्रुटियां परीक्षण कोड में अलग-अलग चीजें हैं। अपने कार्य तो थे एक परीक्षण है कि नहीं ज़ोर करता दौरान एक अपवाद फेंकने के लिए, परीक्षण ढांचे का इलाज होता है कि एक के रूप में त्रुटि , बल्कि जोर नहीं करने के लिए एक विफलता की तुलना में।
coredumperror

@CoreDumpError मैं एक विफलता और एक त्रुटि के बीच के अंतर को समझता हूं, लेकिन क्या यह आपको प्रत्येक परीक्षण को एक कोशिश / अपवाद निर्माण के साथ घेरने के लिए मजबूर नहीं करेगा ? या क्या आप ऐसा केवल उन परीक्षणों के लिए करने की सिफारिश करेंगे जो स्पष्ट रूप से कुछ शर्त के तहत एक अपवाद बढ़ाते हैं (जिसका मूल रूप से मतलब यह है कि अपवाद अपेक्षित है )।
फ़ेडरिकोजासन

4
@federicojasson आपने उस दूसरे वाक्य में अपने प्रश्न का बहुत अच्छी तरह से उत्तर दिया। परीक्षणों में त्रुटियों बनाम असफलताओं को क्रमशः "अप्रत्याशित दुर्घटनाओं" बनाम "अनपेक्षित व्यवहार" के रूप में वर्णित किया जा सकता है। आप चाहते हैं कि आपके परीक्षण में कोई त्रुटि दिखाई दे, जब आपका फ़ंक्शन क्रैश हो जाता है, लेकिन ऐसा नहीं है कि जब आप जानते हैं कि यह एक अपवाद है, तो इसे दिया जाएगा जब कुछ इनपुट अलग-अलग इनपुट दिए जाते हैं।
coredumperror

52

बस फ़ंक्शन को कॉल करें। यदि यह एक अपवाद उठाता है, तो इकाई परीक्षण रूपरेखा इसे एक त्रुटि के रूप में चिह्नित करेगी। आप टिप्पणी जोड़ना पसंद कर सकते हैं, जैसे:

sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)

35
विफलताएं और त्रुटियां वैचारिक रूप से अलग हैं। इसके अलावा, चूंकि अजगर अपवाद नियंत्रण प्रवाह के लिए नियमित रूप से उपयोग किया जाता है, यह एक नज़र में समझने के लिए बहुत मुश्किल बना देगा (= परीक्षण कोड की खोज के बिना) यदि आपने अपना तर्क या अपना कोड तोड़ा है ...
mac

1
या तो आपका परीक्षण पास हो जाता है या नहीं होता है। यदि यह पास नहीं होता है, तो आपको इसे ठीक करने के लिए जाना होगा। चाहे यह "विफलता" के रूप में रिपोर्ट किया गया हो या "त्रुटि" ज्यादातर अप्रासंगिक हो। एक अंतर है: मेरे जवाब से आपको स्टैक ट्रेस दिखाई देगा ताकि आप देख सकें कि PathIsNotAValidOne को कहाँ फेंका गया था; स्वीकृत उत्तर के साथ आपके पास यह जानकारी नहीं होगी कि डिबगिंग कठिन होगी। (Py2 को मान लें, सुनिश्चित नहीं है कि Py3 इस पर बेहतर है)।
user9876

19
@ user9876 - नहींं। परीक्षा से बाहर निकलने की स्थिति 3 है (पास / नोपास / त्रुटि), 2 नहीं जैसा कि आप गलत तरीके से मानते हैं। त्रुटियों और विफलताओं के बीच का अंतर पर्याप्त है और उनके साथ ऐसा व्यवहार कर रहा है जैसे कि वे वही हैं जो कि केवल खराब प्रोग्रामिंग है। यदि आप मुझ पर विश्वास नहीं करते हैं, तो बस चारों ओर देखें कि परीक्षण धावक कैसे काम करते हैं और वे किस फैसले के पेड़ को विफल विफलता और त्रुटियों को लागू करते हैं। अजगर के लिए एक अच्छा प्रारंभिक बिंदु है डेकोरेटर pytest में। xfail
मैक

4
मुझे लगता है कि यह इस बात पर निर्भर करता है कि आप यूनिट परीक्षणों का उपयोग कैसे करते हैं। जिस तरह से मेरी टीम इकाई परीक्षणों का उपयोग करती है, उन्हें सभी पास करना होगा। (फुर्तीली प्रोग्रामिंग, एक निरंतर एकीकरण मशीन के साथ जो सभी यूनिट परीक्षण चलाती है)। मुझे पता है कि परीक्षण का मामला "पास", "विफल" या "त्रुटि" की रिपोर्ट कर सकता है। लेकिन एक उच्च स्तर पर वास्तव में मेरी टीम के लिए क्या मायने रखता है "सभी इकाई परीक्षण पास करते हैं?" (यानी "जेनकिंस हरा है?")। इसलिए मेरी टीम के लिए, "असफल" और "त्रुटि" के बीच कोई व्यावहारिक अंतर नहीं है। यदि आप अपनी इकाई परीक्षणों का एक अलग तरीके से उपयोग करते हैं, तो आपकी अलग-अलग आवश्यकताएं हो सकती हैं।
user9876

1
@ user9876 '' फेल 'और' एरर 'के बीच का अंतर "मेरा अस्सिटेंट फेल" और "मेरा इम्तिहान भी नहीं मिलता है" के बीच का अंतर है। मेरे लिए, परीक्षण को ठीक करने के दौरान एक उपयोगी अंतर है, लेकिन मुझे लगता है, जैसा कि आप कहते हैं, सभी के लिए नहीं।
सीएस

14

मैं मूल पोस्टर हूं और मैंने डीजीएच द्वारा उपरोक्त उत्तर को पहली बार कोड में उपयोग किए बिना स्वीकार कर लिया है।

एक बार जब मैंने उपयोग किया तो मुझे एहसास हुआ कि इसे वास्तव में करने के लिए थोड़ा ट्विकिंग की आवश्यकता थी (डीजीएच के लिए निष्पक्ष होने के लिए उसे / उसे कुछ ऐसा ही कहना था "!"

मैंने सोचा कि यह दूसरों के लाभ के लिए यहाँ ट्विक पोस्ट करने लायक था:

    try:
        a = Application("abcdef", "")
    except pySourceAidExceptions.PathIsNotAValidOne:
        pass
    except:
        self.assertTrue(False)

मैं यहाँ क्या करने का प्रयास कर रहा था, यह सुनिश्चित करने के लिए कि यदि रिक्त स्थान के दूसरे तर्क pySourceAidException.PathIsNotAValidOne को उठाया जाएगा के साथ एक आवेदन वस्तु को तत्काल करने का प्रयास किया गया था।

मेरा मानना ​​है कि उपरोक्त कोड (DGH के जवाब पर बहुत अधिक आधारित) का उपयोग करना होगा।


2
चूँकि आप अपने प्रश्न को स्पष्ट कर रहे हैं और इसका उत्तर नहीं दे रहे हैं, आपको इसे संपादित करना चाहिए था (इसका उत्तर नहीं दिया गया) कृपया नीचे मेरा उत्तर देखें।
१३:३३ पर हाईवेलॉन

13
यह मूल समस्या के बिल्कुल विपरीत प्रतीत होता है। self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath)इस मामले में काम करना चाहिए।
एंटनी हैचकिंस

8

आप मॉड्यूल में assertNotRaisesमूल कार्यान्वयन के लगभग 90% का पुन: उपयोग करके परिभाषित कर सकते हैं । इस दृष्टिकोण के साथ, आप एक विधि के साथ समाप्त होते हैं, जो इसकी उलटी विफलता की स्थिति से हटकर, व्यवहारिक रूप से व्यवहार करता है ।assertRaisesunittestassertNotRaisesassertRaises

TLDR और लाइव डेमो

यह एक assertNotRaisesविधि को जोड़ने के लिए आश्चर्यजनक रूप से आसान हो जाता है unittest.TestCase(इस उत्तर को लिखने में मुझे लगभग 4 गुना समय लगा क्योंकि यह कोड था)। यहां कार्रवाई में विधि का लाइव डेमो हैassertNotRaises । ठीक वैसे हीassertRaises , आप या तो एक कॉल करने योग्य पास assertNotRaisesकर सकते हैं, और आप इसे एक withबयान में उपयोग कर सकते हैं । लाइव डेमो में एक परीक्षण के मामले शामिल हैं जो यह प्रदर्शित assertNotRaisesकरता है कि इरादा के अनुसार काम करता है।

विवरण

के कार्यान्वयन assertRaisesमें unittestकाफी जटिल है, लेकिन चालाक उपवर्गीकरण का एक छोटा सा के साथ आप को ओवरराइड और उसके विफलता की स्थिति पलट सकता है।

assertRaisesएक छोटी विधि है जो मूल रूप से सिर्फ unittest.case._AssertRaisesContextकक्षा का एक उदाहरण बनाती है और उसे वापस करती है ( unittest.caseमॉड्यूल में इसकी परिभाषा देखें )। आप अपनी विधि को _AssertNotRaisesContextउप-वर्ग करके _AssertRaisesContextऔर उसकी __exit__विधि को ओवरराइड करके परिभाषित कर सकते हैं :

import traceback
from unittest.case import _AssertRaisesContext

class _AssertNotRaisesContext(_AssertRaisesContext):
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is not None:
            self.exception = exc_value.with_traceback(None)

            try:
                exc_name = self.expected.__name__
            except AttributeError:
                exc_name = str(self.expected)

            if self.obj_name:
                self._raiseFailure("{} raised by {}".format(exc_name,
                    self.obj_name))
            else:
                self._raiseFailure("{} raised".format(exc_name))

        else:
            traceback.clear_frames(tb)

        return True

आम तौर पर आप टेस्ट केस क्लासेस को इनसे विरासत में लेते हैं TestCase। यदि आप इसके बजाय एक उपवर्ग से विरासत में लेते हैं MyTestCase:

class MyTestCase(unittest.TestCase):
    def assertNotRaises(self, expected_exception, *args, **kwargs):
        context = _AssertNotRaisesContext(expected_exception, self)
        try:
            return context.handle('assertNotRaises', args, kwargs)
        finally:
            context = None

आपके सभी परीक्षण मामले अब उनके पास assertNotRaisesउपलब्ध विधि होंगे।


tracebackआपके elseकथन में कहां से आ रहा है?
नं।

1
@ कोई लापता था import। इसका नियत समय
टेल

2
def _assertNotRaises(self, exception, obj, attr):                                                                                                                              
     try:                                                                                                                                                                       
         result = getattr(obj, attr)                                                                                                                                            
         if hasattr(result, '__call__'):                                                                                                                                        
             result()                                                                                                                                                           
     except Exception as e:                                                                                                                                                     
         if isinstance(e, exception):                                                                                                                                           
            raise AssertionError('{}.{} raises {}.'.format(obj, attr, exception)) 

यदि आपको मापदंडों को स्वीकार करने की आवश्यकता है तो संशोधित किया जा सकता है।

जैसे बुलाओ

self._assertNotRaises(IndexError, array, 'sort')

1

मैंने इसे बंदर-पैच के लिए उपयोगी पाया unittestहै:

def assertMayRaise(self, exception, expr):
  if exception is None:
    try:
      expr()
    except:
      info = sys.exc_info()
      self.fail('%s raised' % repr(info[0]))
  else:
    self.assertRaises(exception, expr)

unittest.TestCase.assertMayRaise = assertMayRaise

अपवाद की अनुपस्थिति के लिए परीक्षण करते समय यह आशय स्पष्ट करता है:

self.assertMayRaise(None, does_not_raise)

यह एक लूप में परीक्षण को सरल करता है, जिसे मैं अक्सर खुद को कर पाता हूं:

# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
  self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))

एक बंदर-पैच क्या है?
स्कॉटएमसीसी

1
En.wikipedia.org/wiki/Monkey_patch देखें । आप assertMayRaiseको जोड़ने के बाद unittest.TestSuiteयह सिर्फ unittestपुस्तकालय का हिस्सा है, दिखावा कर सकता है ।
एंडीजॉस्ट

0

यदि आप एक अपवाद कक्षा पास करते हैं assertRaises(), तो एक संदर्भ प्रबंधक प्रदान किया जाता है। यह आपके परीक्षणों की पठनीयता में सुधार कर सकता है:

# raise exception if Application created with bad data
with self.assertRaises(pySourceAidExceptions.PathIsNotAValidOne):
    application = Application("abcdef", "")

यह आपको अपने कोड में त्रुटि मामलों का परीक्षण करने की अनुमति देता है।

इस मामले में, आप परीक्षण कर रहे हैं PathIsNotAValidOneजब आप आवेदन निर्माता को अमान्य पैरामीटर पास करते हैं।


1
नहीं, यह केवल तभी विफल होगा जब संदर्भ प्रबंधक ब्लॉक के भीतर अपवाद नहीं उठाया गया हो। आसानी से 'self.assertRaises (TypeError) के साथ परीक्षण किया जा सकता है: TypeError बढ़ाएँ', जो गुजरता है।
मैथ्यू ट्रेवर

@MatthewTrevor अच्छा कॉल। जैसा कि मुझे याद है, परीक्षण कोड के बजाय सही तरीके से निष्पादित होता है, अर्थात नहीं बढ़ाता है, मैं परीक्षण त्रुटि मामलों का सुझाव दे रहा था। मैंने उसी के अनुसार उत्तर संपादित किया है। उम्मीद है कि मैं लाल रंग से बाहर निकलने के लिए +1 कमा सकता हूं। :)
Hiwaylon

ध्यान दें, यह भी पायथन 2.7 और बाद में है: docs.python.org/2/library/…
qneill

0

आप इस तरह की कोशिश कर सकते हैं। कोशिश करें: self.assertRaises (कोई नहीं, फ़ंक्शन, arg1, arg2) को छोड़कर: पास करें यदि आप कोड को प्रयास ब्लॉक के अंदर नहीं रखते हैं तो यह अपवाद के माध्यम से होगा 'AsErionError: कोई नहीं उठाया गया "और परीक्षण मामला विफल हो जाएगा। टेस्ट केस पास हो जाएगा। अगर अंदर की कोशिश ब्लॉक है जो अपेक्षित व्यवहार है।


0

किसी भी त्रुटि के बिना ऑब्जेक्ट को सुनिश्चित करने के लिए एक सीधा आगे का तरीका ऑब्जेक्ट के प्रकार के उदाहरण का परीक्षण करना है।

यहाँ एक उदाहरण है :

p = SomeClass(param1=_param1_value)
self.assertTrue(isinstance(p, SomeClass))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.