जब एक असफलता सामने आती है तो पायथन की सबसे अच्छी तरह से जारी रहती है


84

संपादित करें: बेहतर उदाहरण के लिए स्विच किया गया, और स्पष्ट किया कि यह एक वास्तविक समस्या क्यों है।

मैं पायथन में इकाई परीक्षण लिखना चाहता हूं जो कि जब कोई दावा विफल हो जाता है तो निष्पादित करना जारी रखता है, ताकि मैं एक ही परीक्षण में कई विफलताओं को देख सकूं। उदाहरण के लिए:

class Car(object):
  def __init__(self, make, model):
    self.make = make
    self.model = make  # Copy and paste error: should be model.
    self.has_seats = True
    self.wheel_count = 3  # Typo: should be 4.

class CarTest(unittest.TestCase):
  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    self.assertEqual(car.make, make)
    self.assertEqual(car.model, model)  # Failure!
    self.assertTrue(car.has_seats)
    self.assertEqual(car.wheel_count, 4)  # Failure!

यहां, परीक्षण का उद्देश्य यह सुनिश्चित करना है कि कार __init__अपने क्षेत्रों को सही ढंग से सेट करे। मैं इसे चार विधियों में तोड़ सकता था (और यह अक्सर एक महान विचार है), लेकिन इस मामले में मुझे लगता है कि इसे एकल विधि के रूप में रखने के लिए अधिक पठनीय है जो एक एकल अवधारणा का परीक्षण करता है ("ऑब्जेक्ट को सही ढंग से प्रारंभ किया गया है")।

अगर हम यह मानते हैं कि विधि को न तोड़ना यहाँ सबसे अच्छा है, तो मुझे एक नई समस्या है: मैं सभी त्रुटियों को एक बार में नहीं देख सकता। जब मैं modelत्रुटि को ठीक करता हूं और परीक्षण को फिर से चलाता हूं , तो wheel_countत्रुटि दिखाई देती है। जब मैं पहली बार परीक्षा चलाता हूं, तो मुझे दोनों त्रुटियों को देखने के लिए समय की बचत होगी।

तुलना के लिए, Google का C ++ यूनिट परीक्षण ढांचा गैर-घातक दावे और घातक दावे के बीच अंतर करता है:EXPECT_*ASSERT_*

दावे जोड़े में आते हैं जो एक ही चीज़ का परीक्षण करते हैं लेकिन वर्तमान फ़ंक्शन पर अलग-अलग प्रभाव डालते हैं। ASSERT_ * संस्करण विफल होने पर घातक विफलता उत्पन्न करते हैं, और वर्तमान फ़ंक्शन को निरस्त करते हैं। EXPECT_ * संस्करण गैर-विफल विफलता उत्पन्न करते हैं, जो वर्तमान फ़ंक्शन को समाप्त नहीं करता है। आमतौर पर EXPECT_ * को प्राथमिकता दी जाती है, क्योंकि वे एक परीक्षण में एक से अधिक विफलताओं को रिपोर्ट करने की अनुमति देते हैं। हालाँकि, आपको ASSERT_ * का उपयोग करना चाहिए, अगर यह तब तक जारी रखने का कोई मतलब नहीं है जब प्रश्न में दावा विफल हो जाता है।

क्या EXPECT_*पायथन में समान व्यवहार प्राप्त करने का एक तरीका है unittest? यदि नहीं unittest, तो क्या एक और पायथन यूनिट टेस्ट फ्रेमवर्क है जो इस व्यवहार का समर्थन करता है?


संयोग से, मैं इस बारे में उत्सुक था कि कितने वास्तविक जीवन परीक्षण गैर-घातक दावे से लाभान्वित हो सकते हैं, इसलिए मैंने कुछ कोड उदाहरणों को देखा (Google कोड खोज, RIP के बजाय खोज कोड का उपयोग करने के लिए 2014-08-19 संपादित किया गया)। पहले पृष्ठ से 10 बेतरतीब ढंग से चयनित परिणामों में से, सभी में परीक्षण शामिल थे जो एक ही परीक्षण विधि में कई स्वतंत्र अभिकथन करते थे। सभी गैर-घातक दावे से लाभान्वित होंगे।


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

भविष्य के संदर्भ के लिए, मेरा मानना ​​है कि यह वर्तमान प्रणाली पर समान खोज है, लेकिन परिणाम अब ऊपर वर्णित नहीं हैं।
जेडएडी-मैन

2
@ डेविड, मैंने कुछ भी करने का प्रयास नहीं किया। "केवल एक दावा प्रति विधि" दृष्टिकोण मेरे लिए बहुत ही कठोर हठधर्मी लगता है, लेकिन एकमात्र व्यावहारिक (और बनाए रखने योग्य) समाधान एंथोनी का "पकड़ और जोड़ना" सुझाव प्रतीत होता है। हालांकि, यह मेरे लिए बहुत बदसूरत है, इसलिए मैं बस प्रति पद्धति के साथ कई जोर लगाता हूं, और मुझे सभी विफलताओं को खोजने के लिए जरूरत से ज्यादा बार रनिंग टेस्ट के साथ रहना होगा।
ब्रूस क्रिस्टेंसेन

पायटेस्ट नामक पायथन परीक्षण ढांचा काफी सहज है, और डिफ़ॉल्ट रूप से सभी मुखर विफलताओं को दर्शाता है। यह आपके द्वारा सामना की जा रही समस्या का एक काम हो सकता है।
सूर्य शेखर चक्रवर्ती

जवाबों:


9

शायद आप जो करना चाहते हैं वह व्युत्पन्न है unittest.TestCaseक्योंकि वह वर्ग जब जोर देने पर विफल होता है। आपको अपने TestCaseफेंक न देने के लिए फिर से आर्किटेक्ट करना होगा (हो सकता है कि इसके बजाय विफलताओं की सूची रखें)। री-आर्किटेक्चरिंग सामान अन्य मुद्दों का कारण बन सकता है जिन्हें आपको हल करना होगा। उदाहरण के लिए आप TestSuiteअपने द्वारा किए गए परिवर्तनों के समर्थन में परिवर्तन करने के लिए प्राप्त करने की आवश्यकता को समाप्त कर सकते हैं TestCase


1
मुझे लगा कि यह शायद अंतिम उत्तर होगा, लेकिन मैं अपने ठिकानों को कवर करना चाहता था और देखना चाहता था कि मुझे कुछ याद नहीं है। धन्यवाद!
ब्रूस क्रिस्टेंसेन

4
मैं कहूंगा कि यह TestCaseनरम कथनों को लागू करने के लिए ओवरराइड करने के लिए एक ओवरकिल है - वे विशेष रूप से अजगर में बनाने के लिए आसान हैं: बस अपने सभी AssertionErrorएस (शायद एक साधारण लूप में) पकड़ें, और उन्हें एक सूची या सेट में संग्रहीत करें , फिर उन सभी को एक बार में विफल कर दें। बारीकियों के लिए @Anthony Batchelor का जवाब देखें।
डस्कॉर्डस

2
@dscordas इस पर निर्भर करता है कि क्या यह एक बंद परीक्षा के लिए है या यदि आप अधिकांश परीक्षणों के लिए यह क्षमता चाहते हैं।
आहारबुद्ध

43

गैर-घातक दावे करने का एक और तरीका है कि अपवाद अपवाद को पकड़ना और एक सूची में अपवादों को संग्रहीत करना। फिर उस सूची को फाड़ के भाग के रूप में खाली है।

import unittest

class Car(object):
  def __init__(self, make, model):
    self.make = make
    self.model = make  # Copy and paste error: should be model.
    self.has_seats = True
    self.wheel_count = 3  # Typo: should be 4.

class CarTest(unittest.TestCase):
  def setUp(self):
    self.verificationErrors = []

  def tearDown(self):
    self.assertEqual([], self.verificationErrors)

  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    try: self.assertEqual(car.make, make)
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertEqual(car.model, model)  # Failure!
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertTrue(car.has_seats)
    except AssertionError, e: self.verificationErrors.append(str(e))
    try: self.assertEqual(car.wheel_count, 4)  # Failure!
    except AssertionError, e: self.verificationErrors.append(str(e))

if __name__ == "__main__":
    unittest.main()

2
मुझे यकीन है कि मैं आपसे सहमत हूँ। इस प्रकार सेलेनियम अजगर बैकएंड में सत्यापन त्रुटियों से निपटता है।
एंथोनी बैटका स्नातक

हां, इस समाधान के साथ समस्या यह है कि सभी आवेषणों को त्रुटियों (असफल नहीं) के रूप में गिना जाता है और त्रुटियों को प्रस्तुत करने के तरीके वास्तव में उपयोग करने योग्य नहीं हैं। वैसे भी एक तरीका है और रेंडर फ़ंक्शन को आसानी से सुधारा जा सकता है
eMarine

मैं इस समाधान का उपयोग डाइटबुड्डा के उत्तर के साथ संयोजन में सभी प्रयासों को ओवरराइड करके या unittest.TestCaseब्लॉकों को छोड़कर कर रहा हूं ।
thodic

जटिल परीक्षा पैटर्न के लिए यह सबसे अच्छी त्रुटि को हरा देने के लिए सबसे अच्छा समाधान है, लेकिन यह परीक्षण को सभी कोशिशों / बहानों के साथ बदसूरत बनाता है। यह बहुत सारे परीक्षणों और एक जटिल एकल परीक्षण के बीच एक परंपरा है। मैंने इसके बजाय एक त्रुटि को वापस करना शुरू कर दिया है। इसलिए मैं एक परीक्षण में पूरे टेस्टपैटर्न का परीक्षण कर सकता हूं और अपने साथी आकस्मिक अजगर डेवलपर्स के लिए पठनीयता रख सकता हूं।
मोर्टेनब

यह बेहद चालाक है, इसलिए आपसे नफरत करता है।
कोर्टिमेस

30

एक विकल्प एक बार में सभी मूल्यों पर मुखर है।

उदाहरण के लिए:

class CarTest(unittest.TestCase):
  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    self.assertEqual(
            (car.make, car.model, car.has_seats, car.wheel_count),
            (make, model, True, 4))

इस परीक्षण से उत्पादन होगा:

======================================================================
FAIL: test_init (test.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\temp\py_mult_assert\test.py", line 17, in test_init
    (make, model, True, 4))
AssertionError: Tuples differ: ('Ford', 'Ford', True, 3) != ('Ford', 'Model T', True, 4)

First differing element 1:
Ford
Model T

- ('Ford', 'Ford', True, 3)
?           ^ -          ^

+ ('Ford', 'Model T', True, 4)
?           ^  ++++         ^

इससे पता चलता है कि मॉडल और व्हील काउंट दोनों ही गलत हैं।


यह स्मार्ट है। सबसे अच्छा समाधान जो मैंने अब तक पाया है।
चेन नी

7

यह एक एकल इकाई परीक्षण में कई जोर देने के लिए एक विरोधी पैटर्न माना जाता है। एक एकल इकाई परीक्षण से केवल एक चीज का परीक्षण करने की उम्मीद की जाती है। शायद आप बहुत अधिक परीक्षण कर रहे हैं। इस परीक्षण को कई परीक्षणों में विभाजित करने पर विचार करें। इस तरह आप प्रत्येक परीक्षा को ठीक से नाम दे सकते हैं।

हालांकि कभी-कभी, एक ही समय में कई चीजों की जांच करना ठीक है। उदाहरण के लिए जब आप एक ही वस्तु के गुणों का मूल्यांकन कर रहे हों। उस स्थिति में आप वास्तव में यह दावा कर रहे हैं कि क्या वह वस्तु सही है। ऐसा करने का एक तरीका एक कस्टम सहायक विधि लिखना है जो जानता है कि उस वस्तु पर कैसे जोर दिया जाए। आप उस तरीके को इस तरह से लिख सकते हैं कि यह सभी विफल गुणों को दिखाता है या उदाहरण के लिए अपेक्षित ऑब्जेक्ट की पूर्ण स्थिति और वास्तविक ऑब्जेक्ट की पूर्ण स्थिति दिखाता है जब एक एस्टर विफल हो जाता है।


1
@ ब्रूस: एक जोरदार असफल या सफल होना चाहिए। बीच में कभी कुछ नहीं। परीक्षण भरोसेमंद, पठनीय और बनाए रखने योग्य होना चाहिए। एक असफल मुखर जो परीक्षण में विफल नहीं होता है वह एक बुरा विचार है। यह आपके परीक्षणों को अत्यधिक जटिल बनाता है (जो पठनीयता और स्थिरता को कम करता है) और ऐसे परीक्षण जिन्हें 'विफल होने की अनुमति' है, उन्हें अनदेखा करना आसान बनाता है, जिसका अर्थ है कि वे भरोसेमंद नहीं हैं।
स्टीवन

8
किसी भी कारण से बाकी परीक्षा नहीं चल सकती है और यह अभी भी घातक है। मुझे लगता है कि आप सभी संभावित विफलताओं को एकत्र करने के पक्ष में कहीं न कहीं विफलता की वापसी में देरी कर सकते हैं।
आहारबुद्ध

5
मुझे लगता है कि हम दोनों एक ही बात कह रहे हैं। मैं चाहता हूं कि परीक्षा में असफल होने के लिए हर असफलता का दावा किया जाए; यह सिर्फ इतना है कि मैं चाहता हूं कि जब टेस्ट विधि वापस आ जाए, तो इसके बजाय विफलता का परीक्षण किया जाए, जब @dietbuddhan ने उल्लेख किया है। यह विधि के सभी अभिसरणों का परीक्षण करने की अनुमति देगा , ताकि मैं एक शॉट में सभी विफलताओं को देख (और ठीक कर) सकूं। परीक्षण अभी भी भरोसेमंद, पठनीय और बनाए रखने योग्य है (और भी बहुत कुछ, वास्तव में)।
ब्रूस क्रिस्टेंसेन

10
वह यह नहीं कह रहा है कि परीक्षा में विफल नहीं होना चाहिए जब आप जोर से मारते हैं, तो वह कह रहा है कि विफलता को अन्य जांचों को रोकना नहीं चाहिए। उदाहरण के लिए, अभी मैं परीक्षण कर रहा हूं कि विशेष निर्देशिकाएं उपयोगकर्ता, समूह और अन्य लेखन योग्य हैं। प्रत्येक एक अलग मुखर है। परीक्षण आउटपुट से यह जानना उपयोगी होगा कि सभी तीन मामले विफल हो रहे हैं, इसलिए मैं उन्हें "एक उपयोगकर्ता द्वारा लिखने योग्य नहीं होने के बजाय पथ को ठीक करने के लिए" फिर से परीक्षण करने के बजाय एक ही कॉल कर सकता हूं। समूह-योग्य नहीं ”और इसी तरह। हालाँकि मुझे लगता है कि मैंने सिर्फ यह तर्क दिया था कि उन्हें अलग-अलग टेस्ट ...
टिम कीटिंग

8
सिर्फ इसलिए कि लाइब्रेरी को यूनीटेस्ट कहा जाता है, इसका मतलब यह नहीं है कि परीक्षण एक पृथक इकाई परीक्षण है। यूनीटेस्ट मॉड्यूल, साथ ही पाइस्टेस्ट और नाक और अन्य, सिस्टम परीक्षण, एकीकरण परीक्षण, आदि के लिए बहुत अच्छा काम करते हैं। एक कैवेट होने के साथ कि आप केवल एक बार असफल हो सकते हैं। यह वास्तव में कष्टप्रद है। मैं वास्तव में सभी मुखर कार्यों को देखना चाहता हूं, या तो एक पैरामीटर जोड़ें, जो आपको विफलता के साथ जारी रखने की अनुमति देता है, या आशावादी कार्यों के दोहराव को कहा जाता है, जिसे अपेक्षित कहा जाता है, ऐसा काम करें। फिर यह सबसे आसान होगा कि बड़े से बड़े कार्यात्मक परीक्षण को एकसाथ लिखें।
ओकेन

7

पायथन 3.4 के बाद से आप सबस्ट्रेट्स का उपयोग भी कर सकते हैं :

def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    with self.subTest(msg='Car.make check'):
        self.assertEqual(car.make, make)
    with self.subTest(msg='Car.model check'):
        self.assertEqual(car.model, model)
    with self.subTest(msg='Car.has_seats check'):
        self.assertTrue(car.has_seats)
    with self.subTest(msg='Car.wheel_count check'):
        self.assertEqual(car.wheel_count, 4)

( msgपैरामीटर का उपयोग अधिक आसानी से निर्धारित करने के लिए किया जाता है कि कौन सा परीक्षण विफल रहा।)

आउटपुट:

======================================================================
FAIL: test_init (__main__.CarTest) [Car.model check]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 23, in test_init
    self.assertEqual(car.model, model)
AssertionError: 'Ford' != 'Model T'
- Ford
+ Model T


======================================================================
FAIL: test_init (__main__.CarTest) [Car.wheel_count check]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 27, in test_init
    self.assertEqual(car.wheel_count, 4)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=2)

1
यह अब मौजूदा कोड में छोड़ने के लिए सबसे आसान के रूप में स्वीकृत उत्तर होना चाहिए।
माइकल स्कॉट कटहबर्ट

5

प्रत्येक आसन को एक अलग विधि से करें।

class MathTest(unittest.TestCase):
  def test_addition1(self):
    self.assertEqual(1 + 0, 1)

  def test_addition2(self):
    self.assertEqual(1 + 1, 3)

  def test_addition3(self):
    self.assertEqual(1 + (-1), 0)

  def test_addition4(self):
    self.assertEqaul(-1 + (-1), -1)

5
मुझे पता है कि यह एक संभव समाधान है, लेकिन यह हमेशा व्यावहारिक नहीं है। मैं एक ऐसी चीज की तलाश कर रहा हूं जो एक पूर्व-कोलाइज़ टेस्ट को कई छोटी विधियों में विभाजित किए बिना काम करती है।
ब्रूस क्रिस्टेंसेन

@ ब्रूस क्रिस्टेंसन: यदि वे इतने सामंजस्यपूर्ण हैं तो शायद वे एक कहानी बनाते हैं? और फिर वे doctests, जो वास्तव में बनाया जा सकता है जाएगा विफलता के बाद भी जारी है।
लेन्नर्ट रेगेब्रो

1
मेरे पास परीक्षणों का एक सेट है, कुछ इस तरह से है: 1. लोड डेटा, 2. सही ढंग से लोड किए गए डेटा को जोर दें, 3. डेटा को संशोधित करें, 4. एसेट संशोधन ने सही तरीके से काम किया, 5. संशोधित डेटा को सहेजें, 6. सही तरीके से सहेजे गए डेटा को जोर दें। मैं इस विधि के साथ ऐसा कैसे कर सकता हूं? इसमें डेटा लोड करने का कोई मतलब नहीं है setup(), क्योंकि यह परीक्षणों में से एक है। लेकिन अगर मैं प्रत्येक कार्य को अपने स्वयं के कार्य में लगाता हूं, तो मुझे 3 बार डेटा लोड करना होगा, और यह संसाधनों का एक बड़ा अपशिष्ट है। उस तरह की स्थिति से निपटने का सबसे अच्छा तरीका क्या है?
n

खैर, एक विशिष्ट अनुक्रम का परीक्षण करने वाले परीक्षण उसी परीक्षण विधि में होने चाहिए।
लेन्आर्ट रेगेब्रो

4

PyPI में एक नरम मुखर पैकेज है जिसे कहा जाता है softestजो आपकी आवश्यकताओं को पूरा करेगा। यह विफलताओं को इकट्ठा करके, अपवाद और स्टैक ट्रेस डेटा के संयोजन से काम करता है, और सामान्य unittestआउटपुट के हिस्से के रूप में यह सब रिपोर्टिंग करता है ।

उदाहरण के लिए, यह कोड:

import softest

class ExampleTest(softest.TestCase):
    def test_example(self):
        # be sure to pass the assert method object, not a call to it
        self.soft_assert(self.assertEqual, 'Worf', 'wharf', 'Klingon is not ship receptacle')
        # self.soft_assert(self.assertEqual('Worf', 'wharf', 'Klingon is not ship receptacle')) # will not work as desired
        self.soft_assert(self.assertTrue, True)
        self.soft_assert(self.assertTrue, False)

        self.assert_all()

if __name__ == '__main__':
    softest.main()

... इस कंसोल आउटपुट का उत्पादन करता है:

======================================================================
FAIL: "test_example" (ExampleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 14, in test_example
    self.assert_all()
  File "C:\...\softest\case.py", line 138, in assert_all
    self.fail(''.join(failure_output))
AssertionError: ++++ soft assert failure details follow below ++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The following 2 failures were found in "test_example" (ExampleTest):
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Failure 1 ("test_example" method)
+--------------------------------------------------------------------+
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 10, in test_example
    self.soft_assert(self.assertEqual, 'Worf', 'wharf', 'Klingon is not ship receptacle')
  File "C:\...\softest\case.py", line 84, in soft_assert
    assert_method(*arguments, **keywords)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 829, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 1203, in assertMultiLineEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: 'Worf' != 'wharf'
- Worf
+ wharf
 : Klingon is not ship receptacle

+--------------------------------------------------------------------+
Failure 2 ("test_example" method)
+--------------------------------------------------------------------+
Traceback (most recent call last):
  File "C:\...\softest_test.py", line 12, in test_example
    self.soft_assert(self.assertTrue, False)
  File "C:\...\softest\case.py", line 84, in soft_assert
    assert_method(*arguments, **keywords)
  File "C:\...\Python\Python36-32\lib\unittest\case.py", line 682, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

नोट : मैंने बनाया और बनाए रखा है softest


3

gtest में अपेक्षा बहुत उपयोगी है। यह जिस्ट और कोड में अजगर का तरीका है :

import sys
import unittest


class TestCase(unittest.TestCase):
    def run(self, result=None):
        if result is None:
            self.result = self.defaultTestResult()
        else:
            self.result = result

        return unittest.TestCase.run(self, result)

    def expect(self, val, msg=None):
        '''
        Like TestCase.assert_, but doesn't halt the test.
        '''
        try:
            self.assert_(val, msg)
        except:
            self.result.addFailure(self, sys.exc_info())

    def expectEqual(self, first, second, msg=None):
        try:
            self.failUnlessEqual(first, second, msg)
        except:
            self.result.addFailure(self, sys.exc_info())

    expect_equal = expectEqual

    assert_equal = unittest.TestCase.assertEqual
    assert_raises = unittest.TestCase.assertRaises


test_main = unittest.main

2

मुझे एस्टरोनी-बैटरल द्वारा एशेरियनएयर अपवाद को पकड़ने के लिए दृष्टिकोण पसंद आया। लेकिन सज्जाकारों का उपयोग करते हुए इस दृष्टिकोण में थोड़ा बदलाव और पास / असफल के साथ परीक्षण मामलों की रिपोर्ट करने का एक तरीका भी है।

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest

class UTReporter(object):
    '''
    The UT Report class keeps track of tests cases
    that have been executed.
    '''
    def __init__(self):
        self.testcases = []
        print "init called"

    def add_testcase(self, testcase):
        self.testcases.append(testcase)

    def display_report(self):
        for tc in self.testcases:
            msg = "=============================" + "\n" + \
                "Name: " + tc['name'] + "\n" + \
                "Description: " + str(tc['description']) + "\n" + \
                "Status: " + tc['status'] + "\n"
            print msg

reporter = UTReporter()

def assert_capture(*args, **kwargs):
    '''
    The Decorator defines the override behavior.
    unit test functions decorated with this decorator, will ignore
    the Unittest AssertionError. Instead they will log the test case
    to the UTReporter.
    '''
    def assert_decorator(func):
        def inner(*args, **kwargs):
            tc = {}
            tc['name'] = func.__name__
            tc['description'] = func.__doc__
            try:
                func(*args, **kwargs)
                tc['status'] = 'pass'
            except AssertionError:
                tc['status'] = 'fail'
            reporter.add_testcase(tc)
        return inner
    return assert_decorator



class DecorateUt(unittest.TestCase):

    @assert_capture()
    def test_basic(self):
        x = 5
        self.assertEqual(x, 4)

    @assert_capture()
    def test_basic_2(self):
        x = 4
        self.assertEqual(x, 4)

def main():
    #unittest.main()
    suite = unittest.TestLoader().loadTestsFromTestCase(DecorateUt)
    unittest.TextTestRunner(verbosity=2).run(suite)

    reporter.display_report()


if __name__ == '__main__':
    main()

कंसोल से आउटपुट:

(awsenv)$ ./decorators.py 
init called
test_basic (__main__.DecorateUt) ... ok
test_basic_2 (__main__.DecorateUt) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
=============================
Name: test_basic
Description: None
Status: fail

=============================
Name: test_basic_2
Description: None
Status: pass

1

मुझे @ एंथनी बेटिकल्चर के उत्तर से समस्या थी क्योंकि इसने मुझे try...catchअपनी इकाई परीक्षणों के अंदर उपयोग करने के लिए मजबूर किया होगा । इसके बजाय, मैंने विधि के try...catchओवरराइड में तर्क को समझाया TestCase.assertEqual। यहाँ कोड है:

import unittest
import traceback

class AssertionErrorData(object):

    def __init__(self, stacktrace, message):
        super(AssertionErrorData, self).__init__()
        self.stacktrace = stacktrace
        self.message = message

class MultipleAssertionFailures(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        self.verificationErrors = []
        super(MultipleAssertionFailures, self).__init__( *args, **kwargs )

    def tearDown(self):
        super(MultipleAssertionFailures, self).tearDown()

        if self.verificationErrors:
            index = 0
            errors = []

            for error in self.verificationErrors:
                index += 1
                errors.append( "%s\nAssertionError %s: %s" % ( 
                        error.stacktrace, index, error.message ) )

            self.fail( '\n\n' + "\n".join( errors ) )
            self.verificationErrors.clear()

    def assertEqual(self, goal, results, msg=None):

        try:
            super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )

        except unittest.TestCase.failureException as error:
            goodtraces = self._goodStackTraces()
            self.verificationErrors.append( 
                    AssertionErrorData( "\n".join( goodtraces[:-2] ), error ) )

    def _goodStackTraces(self):
        """
            Get only the relevant part of stacktrace.
        """
        stop = False
        found = False
        goodtraces = []

        # stacktrace = traceback.format_exc()
        # stacktrace = traceback.format_stack()
        stacktrace = traceback.extract_stack()

        # /programming/54499367/how-to-correctly-override-testcase
        for stack in stacktrace:
            filename = stack.filename

            if found and not stop and \
                    not filename.find( 'lib' ) < filename.find( 'unittest' ):
                stop = True

            if not found and filename.find( 'lib' ) < filename.find( 'unittest' ):
                found = True

            if stop and found:
                stackline = '  File "%s", line %s, in %s\n    %s' % ( 
                        stack.filename, stack.lineno, stack.name, stack.line )
                goodtraces.append( stackline )

        return goodtraces

# class DummyTestCase(unittest.TestCase):
class DummyTestCase(MultipleAssertionFailures):

    def setUp(self):
        self.maxDiff = None
        super(DummyTestCase, self).setUp()

    def tearDown(self):
        super(DummyTestCase, self).tearDown()

    def test_function_name(self):
        self.assertEqual( "var", "bar" )
        self.assertEqual( "1937", "511" )

if __name__ == '__main__':
    unittest.main()

परिणाम आउटपुट:

F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 77, in tearDown
    super(DummyTestCase, self).tearDown()
  File "D:\User\Downloads\test.py", line 29, in tearDown
    self.fail( '\n\n' + "\n\n".join( errors ) )
AssertionError: 

  File "D:\User\Downloads\test.py", line 80, in test_function_name
    self.assertEqual( "var", "bar" )
AssertionError 1: 'var' != 'bar'
- var
? ^
+ bar
? ^
 : 

  File "D:\User\Downloads\test.py", line 81, in test_function_name
    self.assertEqual( "1937", "511" )
AssertionError 2: '1937' != '511'
- 1937
+ 511
 : 

सही स्टैकट्रेस कैप्चर के लिए अधिक वैकल्पिक समाधान सही स्टैकट्रेस का निर्माण करके टेस्टकैसे.सैटरएक्वाल () को सही ढंग से ओवरराइड करने पर पोस्ट किया जा सकता है ?


0

मुझे नहीं लगता कि PyUnit के साथ ऐसा करने का कोई तरीका है और PyUnit को इस तरह से विस्तारित नहीं देखना चाहेगा।

मैं प्रति परीक्षण फ़ंक्शन ( या अधिक विशेष रूप से प्रति परीक्षण एक अवधारणा का दावा करना) पर एक जोर देना पसंद करता हूं और test_addition()चार अलग-अलग परीक्षण कार्यों के रूप में फिर से लिखूंगा । यह विफलता पर अधिक उपयोगी जानकारी देगा, अर्थात :

.FF.
======================================================================
FAIL: test_addition_with_two_negatives (__main__.MathTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_addition.py", line 10, in test_addition_with_two_negatives
    self.assertEqual(-1 + (-1), -1)
AssertionError: -2 != -1

======================================================================
FAIL: test_addition_with_two_positives (__main__.MathTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_addition.py", line 6, in test_addition_with_two_positives
    self.assertEqual(1 + 1, 3)  # Failure!
AssertionError: 2 != 3

----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=2)

यदि आप तय करते हैं कि यह दृष्टिकोण आपके लिए नहीं है, तो आपको यह उत्तर मददगार लग सकता है ।

अपडेट करें

ऐसा लगता है कि आप अपने अद्यतन प्रश्न के साथ दो अवधारणाओं का परीक्षण कर रहे हैं और मैं इन्हें दो इकाई परीक्षणों में विभाजित कर दूंगा। पहला यह है कि नए ऑब्जेक्ट के निर्माण पर मापदंडों को संग्रहीत किया जा रहा है। यह दो दावे, एक के लिए makeऔर एक के लिए होगाmodel । यदि पहला विफल हो जाता है, तो स्पष्ट रूप से इसे ठीक करने की आवश्यकता है, चाहे दूसरा पास हो या विफल इस मोड़ पर अप्रासंगिक है।

दूसरी अवधारणा अधिक संदिग्ध है ... आप परीक्षण कर रहे हैं कि क्या कुछ डिफ़ॉल्ट मान आरंभ किए गए हैं। क्यों ? इन मूल्यों का परीक्षण करने के लिए यह अधिक उपयोगी होगा कि वे वास्तव में उपयोग किए जाते हैं (और यदि उनका उपयोग नहीं किया जाता है, तो वे वहां क्यों हैं?)।

ये दोनों परीक्षण विफल हो जाते हैं, और दोनों चाहिए। जब मैं यूनिट-परीक्षण कर रहा होता हूं, तो मैं सफलता की तुलना में विफलता में कहीं अधिक रुचि रखता हूं क्योंकि मैं जहां ध्यान केंद्रित करने की आवश्यकता होती है।

FF
======================================================================
FAIL: test_creation_defaults (__main__.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_car.py", line 25, in test_creation_defaults
    self.assertEqual(self.car.wheel_count, 4)  # Failure!
AssertionError: 3 != 4

======================================================================
FAIL: test_creation_parameters (__main__.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_car.py", line 20, in test_creation_parameters
    self.assertEqual(self.car.model, self.model)  # Failure!
AssertionError: 'Ford' != 'Model T'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=2)

तो क्या आप चार कार्यों में Car.test_init को तोड़ देंगे?
ब्रूस क्रिस्टेंसेन

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

0

मुझे पता है कि यह सवाल सचमुच वर्षों पहले पूछा गया था, लेकिन अब (कम से कम) दो पायथन पैकेज हैं जो आपको ऐसा करने की अनुमति देते हैं।

एक सबसे नरम है: https://pypi.org/project/softest/

दूसरा है पायथन-डिलेय्ड-ऐसर: https://github.com/pr4bh4sh/python-delayed-assert

मैंने या तो उपयोग नहीं किया है, लेकिन वे मेरे समान सुंदर दिखते हैं।

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