अजगर में यूनिट टेस्ट से डेटा आउटपुट


115

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

उदाहरण के लिए, मान लें कि आपके पास एक वर्ग फू था, और टेस्टाटा नामक सूची से डेटा का उपयोग करके एक विधि बार का परीक्षण कर रहे थे:

class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1)
            self.assertEqual(f.bar(t2), 2)

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

जवाबों:


73

किसी के लिए बहुत देर से जवाब, जो मेरी तरह, एक सरल और त्वरित उत्तर की तलाश में यहां आता है।

पायथन 2.7 में आप msgइस तरह के त्रुटि संदेश में जानकारी जोड़ने के लिए एक अतिरिक्त पैरामीटर का उपयोग कर सकते हैं :

self.assertEqual(f.bar(t2), 2, msg='{0}, {1}'.format(t1, t2))

यहां ऑफिशियल डॉक्स


1
पायथन 3 में भी काम करता है।
MrDBA

18
डॉक्स इस ओर इशारा करते हैं लेकिन यह स्पष्ट रूप से ध्यान देने योग्य है: डिफ़ॉल्ट रूप से, यदि msgइसका उपयोग किया जाता है, तो यह सामान्य त्रुटि संदेश को बदल देगा। करने के लिए msgसामान्य त्रुटि संदेश के साथ जोड़ दिया, आप भी सेट करने की जरूरत है TestCase.longMessage सही पर
Catalin Iacob

1
यह जानना अच्छा है कि हम एक कस्टम त्रुटि संदेश पास कर सकते हैं, लेकिन मुझे कुछ संदेश प्रिंट करने में रुचि थी, चाहे वह त्रुटि हो।
हैरी मोरेनो

5
@CatalinIacob द्वारा टिप्पणी अजगर 2.x पर लागू होती है। पायथन 3.x में, TestCase.longMessage डिफॉल्ट करता है True
ndmeiri

70

हम इसके लिए लॉगिंग मॉड्यूल का उपयोग करते हैं।

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

import logging
class SomeTest( unittest.TestCase ):
    def testSomething( self ):
        log= logging.getLogger( "SomeTest.testSomething" )
        log.debug( "this= %r", self.this )
        log.debug( "that= %r", self.that )
        # etc.
        self.assertEquals( 3.14, pi )

if __name__ == "__main__":
    logging.basicConfig( stream=sys.stderr )
    logging.getLogger( "SomeTest.testSomething" ).setLevel( logging.DEBUG )
    unittest.main()

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

हालांकि, मेरा पसंदीदा तरीका डिबगिंग पर बहुत समय बिताना नहीं है, लेकिन समस्या को उजागर करने के लिए इसे और अधिक बारीक परीक्षण लिखने में खर्च करना है।


क्या होगा अगर मैं टेस्टोस्टोमेटिंग के अंदर एक विधि फू को कॉल करता हूं और यह कुछ लॉग करता है। मैं लकड़हारे को फू से गुजारे बिना उसके लिए आउटपुट कैसे देख सकता हूं?
सिमाओ

@ सिमाओ: क्या है foo? एक अलग समारोह? एक विधि का कार्य SomeTest? पहले मामले में, एक फ़ंक्शन का अपना लकड़हारा हो सकता है। दूसरे मामले में, अन्य विधि फ़ंक्शन का अपना लकड़हारा हो सकता है। क्या आप जानते हैं कि loggingपैकेज कैसे काम करता है? मल्टीपल लॉगर आदर्श है।
S.Lott

8
मैंने आपके द्वारा निर्दिष्ट सटीक तरीके से लॉगिंग की है। मुझे लगता है कि यह काम कर रहा है, लेकिन मुझे आउटपुट कहां दिखाई देता है? यह कंसोल को आउटपुट नहीं कर रहा है। मैंने इसे एक फ़ाइल में लॉग करने के साथ कॉन्फ़िगर करने की कोशिश की, लेकिन यह किसी भी आउटपुट का उत्पादन नहीं करता है।
मिकी 1

"मेरी पसंदीदा विधि, हालांकि, डिबगिंग पर बहुत समय बिताने के लिए नहीं है, लेकिन समस्या को उजागर करने के लिए इसे और अधिक सूक्ष्म परीक्षण लिखने में खर्च करें।" -- सही कहा!
सेठ

34

आप सरल प्रिंट स्टेटमेंट, या लेखन के किसी अन्य तरीके का उपयोग कर सकते हैं। आप अपने परीक्षणों में कहीं भी पायथन डिबगर को भी आमंत्रित कर सकते हैं।

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

नाक में स्विचेस में उल्लिखित चर दिखाने के लिए या विफल परीक्षणों पर डिबगर को स्वचालित रूप से दिखाने के लिए स्विच भी हैं। उदाहरण के लिए -s( --nocapture) स्टडआउट पर कब्जा करने से रोकता है।


दुर्भाग्य से, नाक लॉगिंग स्टैडआउट में लिखे गए लॉग को इकट्ठा करने के लिए नहीं लगता है / लॉगिंग फ्रेमवर्क का उपयोग करके गलत तरीके से। मेरे पास है printऔर log.debug()एक दूसरे के बगल, और स्पष्ट रूप से पर बारी DEBUGसे जड़ में प्रवेश setUp()विधि है, लेकिन केवल printउत्पादन शो।
haridsv

7
nosetests -sस्टडआउट की सामग्री से पता चलता है कि क्या कोई त्रुटि है या नहीं - कुछ मुझे उपयोगी लगता है।
hargriffle

मुझे नाक डॉक्स में स्वचालित रूप से चर दिखाने के लिए स्विच नहीं मिल सकते हैं। क्या आप मुझे उनका वर्णन करने के लिए कुछ कर सकते हैं?
एबीएम

मुझे अपने आप से नाक या एकतरफा रूप से चर दिखाने का तरीका नहीं पता है। मैं उन चीजों को प्रिंट करता हूं जो मैं अपने परीक्षणों में देखना चाहता हूं।
नेड बैचेल्ड

16

मुझे नहीं लगता कि यह आपकी तलाश है, चर मानों को प्रदर्शित करने का कोई तरीका नहीं है जो विफल नहीं होता है, लेकिन इससे आपको परिणाम प्राप्त करने के तरीके के करीब पहुंचने में मदद मिल सकती है।

आप परिणाम विश्लेषण और प्रसंस्करण के लिए TestRunner.run () द्वारा लौटाए गए TestResult ऑब्जेक्ट का उपयोग कर सकते हैं । विशेष रूप से, TestResult.errors और TestResult.failures

TestResults ऑब्जेक्ट के बारे में:

http://docs.python.org/library/unittest.html#id3

और कुछ कोड आपको सही दिशा में इंगित करने के लिए:

>>> import random
>>> import unittest
>>>
>>> class TestSequenceFunctions(unittest.TestCase):
...     def setUp(self):
...         self.seq = range(5)
...     def testshuffle(self):
...         # make sure the shuffled sequence does not lose any elements
...         random.shuffle(self.seq)
...         self.seq.sort()
...         self.assertEqual(self.seq, range(10))
...     def testchoice(self):
...         element = random.choice(self.seq)
...         error_test = 1/0
...         self.assert_(element in self.seq)
...     def testsample(self):
...         self.assertRaises(ValueError, random.sample, self.seq, 20)
...         for element in random.sample(self.seq, 5):
...             self.assert_(element in self.seq)
...
>>> suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
>>> testResult = unittest.TextTestRunner(verbosity=2).run(suite)
testchoice (__main__.TestSequenceFunctions) ... ERROR
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... FAIL

======================================================================
ERROR: testchoice (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 11, in testchoice
ZeroDivisionError: integer division or modulo by zero

======================================================================
FAIL: testshuffle (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 8, in testshuffle
AssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

----------------------------------------------------------------------
Ran 3 tests in 0.031s

FAILED (failures=1, errors=1)
>>>
>>> testResult.errors
[(<__main__.TestSequenceFunctions testMethod=testchoice>, 'Traceback (most recent call last):\n  File "<stdin>"
, line 11, in testchoice\nZeroDivisionError: integer division or modulo by zero\n')]
>>>
>>> testResult.failures
[(<__main__.TestSequenceFunctions testMethod=testshuffle>, 'Traceback (most recent call last):\n  File "<stdin>
", line 8, in testshuffle\nAssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n')]
>>>

5

एक अन्य विकल्प - एक डिबगर शुरू करें जहां परीक्षण विफल हो जाता है।

टेस्टबॉय के साथ अपने परीक्षणों को चलाने की कोशिश करें (यह बिना बदलावों के आपका सबसे उपयुक्त सूट चलाएगा), और आप एक परीक्षण में विफल होने पर डिबगर खोलने के लिए '--debug' कमांड लाइन स्विच का उपयोग कर सकते हैं।

यहाँ खिड़कियों पर एक टर्मिनल सत्र है:

C:\work> testoob tests.py --debug
F
Debugging for failure in test: test_foo (tests.MyTests.test_foo)
> c:\python25\lib\unittest.py(334)failUnlessEqual()
-> (msg or '%r != %r' % (first, second))
(Pdb) up
> c:\work\tests.py(6)test_foo()
-> self.assertEqual(x, y)
(Pdb) l
  1     from unittest import TestCase
  2     class MyTests(TestCase):
  3       def test_foo(self):
  4         x = 1
  5         y = 2
  6  ->     self.assertEqual(x, y)
[EOF]
(Pdb)

2
नाक ( nose.readthedocs.org/en/latest/index.html ) एक और ढांचा है जो 'आरंभ डिबगर सत्र' विकल्प प्रदान करता है। मैं इसे '-sx --pdb --pdb-विफलताओं' के साथ चलाता हूं, जो आउटपुट नहीं खाता है, पहली विफलता के बाद बंद हो जाता है, और अपवादों और परीक्षण विफलताओं पर पीडीबी में गिर जाता है। जब तक मैं आलसी और एक पाश में परीक्षण नहीं कर रहा हूं, यह समृद्ध त्रुटि संदेशों के लिए मेरी आवश्यकता को हटा दिया है।
१०:०१ पर जुलिटब्लॉक

5

मेरे द्वारा उपयोग की जाने वाली विधि वास्तव में सरल है। मैं इसे एक चेतावनी के रूप में लॉग करता हूं इसलिए यह वास्तव में दिखाई देगा।

import logging

class TestBar(unittest.TestCase):
    def runTest(self):

       #this line is important
       logging.basicConfig()
       log = logging.getLogger("LOG")

       for t1, t2 in testdata:
         f = Foo(t1)
         self.assertEqual(f.bar(t2), 2)
         log.warning(t1)

यदि परीक्षण सफल हुआ तो क्या यह काम करेगा? मेरे मामले में चेतावनी केवल तभी दिखाई दे रही है जब परीक्षण विफल हो जाता है
श्रेया मारिया

@ShreyaMaria हाँ यह होगा
Orane

5

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

कुछ इस तरह से:

log1 = dict()
class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1) 
            if f.bar(t2) != 2: 
                log1("TestBar.runTest") = (f, t1, t2)
                self.fail("f.bar(t2) != 2")

उत्तर के लिए धन्यवाद। उन्होंने मुझे अजगर में यूनिट परीक्षणों से जानकारी रिकॉर्ड करने के लिए कुछ वैकल्पिक विचार दिए हैं।


2

लॉगिंग का उपयोग करें:

import unittest
import logging
import inspect
import os

logging_level = logging.INFO

try:
    log_file = os.environ["LOG_FILE"]
except KeyError:
    log_file = None

def logger(stack=None):
    if not hasattr(logger, "initialized"):
        logging.basicConfig(filename=log_file, level=logging_level)
        logger.initialized = True
    if not stack:
        stack = inspect.stack()
    name = stack[1][3]
    try:
        name = stack[1][0].f_locals["self"].__class__.__name__ + "." + name
    except KeyError:
        pass
    return logging.getLogger(name)

def todo(msg):
    logger(inspect.stack()).warning("TODO: {}".format(msg))

def get_pi():
    logger().info("sorry, I know only three digits")
    return 3.14

class Test(unittest.TestCase):

    def testName(self):
        todo("use a better get_pi")
        pi = get_pi()
        logger().info("pi = {}".format(pi))
        todo("check more digits in pi")
        self.assertAlmostEqual(pi, 3.14)
        logger().debug("end of this test")
        pass

उपयोग:

# LOG_FILE=/tmp/log python3 -m unittest LoggerDemo
.
----------------------------------------------------------------------
Ran 1 test in 0.047s

OK
# cat /tmp/log
WARNING:Test.testName:TODO: use a better get_pi
INFO:get_pi:sorry, I know only three digits
INFO:Test.testName:pi = 3.14
WARNING:Test.testName:TODO: check more digits in pi

यदि आप सेट नहीं करते हैं LOG_FILE, तो लॉगिंग को मिल जाएगा stderr


2

आप इसके लिए loggingमॉड्यूल का उपयोग कर सकते हैं ।

इसलिए यूनिट टेस्ट कोड में, उपयोग करें:

import logging as log

def test_foo(self):
    log.debug("Some debug message.")
    log.info("Some info message.")
    log.warning("Some warning message.")
    log.error("Some error message.")

डिफ़ॉल्ट चेतावनियों और त्रुटियों के लिए आउटपुट किया जाता है /dev/stderr, इसलिए उन्हें कंसोल पर दिखाई देना चाहिए।

लॉग को अनुकूलित करने के लिए (जैसे स्वरूपण), निम्न नमूने का प्रयास करें:

# Set-up logger
if args.verbose or args.debug:
    logging.basicConfig( stream=sys.stdout )
    root = logging.getLogger()
    root.setLevel(logging.INFO if args.verbose else logging.DEBUG)
    ch = logging.StreamHandler(sys.stdout)
    ch.setLevel(logging.INFO if args.verbose else logging.DEBUG)
    ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
    root.addHandler(ch)
else:
    logging.basicConfig(stream=sys.stderr)

2

मैं इन मामलों में क्या करता हूं log.debug(), मेरे आवेदन में कुछ संदेशों के साथ है। चूंकि डिफ़ॉल्ट लॉगिंग स्तर है WARNING, ऐसे संदेश सामान्य निष्पादन में दिखाई नहीं देते हैं।

फिर, एकतरफा में मैं लॉगिंग स्तर को बदल देता हूं DEBUG, ताकि उन्हें चलाते समय ऐसे संदेश दिखाई दें।

import logging

log.debug("Some messages to be shown just when debugging or unittesting")

Unittests में:

# Set log level
loglevel = logging.DEBUG
logging.basicConfig(level=loglevel)



एक पूर्ण उदाहरण देखें:

यह daikiri.pyएक बुनियादी वर्ग है , जो अपने नाम और मूल्य के साथ एक डकीरी लागू करता है। एक विधि make_discount()है जो किसी विशिष्ट छूट को लागू करने के बाद उस विशिष्ट दिकिरी की कीमत लौटाती है

import logging

log = logging.getLogger(__name__)

class Daikiri(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def make_discount(self, percentage):
        log.debug("Deducting discount...")  # I want to see this message
        return self.price * percentage

फिर, मैं एक unittest बनाता हूँ test_daikiri.pyजो इसके उपयोग की जाँच करता है:

import unittest
import logging
from .daikiri import Daikiri


class TestDaikiri(unittest.TestCase):
    def setUp(self):
        # Changing log level to DEBUG
        loglevel = logging.DEBUG
        logging.basicConfig(level=loglevel)

        self.mydaikiri = Daikiri("cuban", 25)

    def test_drop_price(self):
        new_price = self.mydaikiri.make_discount(0)
        self.assertEqual(new_price, 0)

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

इसलिए जब मैं इसे निष्पादित करता हूं तो मुझे log.debugसंदेश मिलते हैं :

$ python -m test_daikiri
DEBUG:daikiri:Deducting discount...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

1

इंस्पेक्ट.ट्रेस आपको एक अपवाद के बाद स्थानीय चर प्राप्त करने देगा। फिर आप पोस्टमार्टम के दौरान परीक्षा के लिए उन स्थानीय चरों को बचाने के लिए निम्नलिखित की तरह एक डेकोरेटर के साथ यूनिट परीक्षणों को लपेट सकते हैं।

import random
import unittest
import inspect


def store_result(f):
    """
    Store the results of a test
    On success, store the return value.
    On failure, store the local variables where the exception was thrown.
    """
    def wrapped(self):
        if 'results' not in self.__dict__:
            self.results = {}
        # If a test throws an exception, store local variables in results:
        try:
            result = f(self)
        except Exception as e:
            self.results[f.__name__] = {'success':False, 'locals':inspect.trace()[-1][0].f_locals}
            raise e
        self.results[f.__name__] = {'success':True, 'result':result}
        return result
    return wrapped

def suite_results(suite):
    """
    Get all the results from a test suite
    """
    ans = {}
    for test in suite:
        if 'results' in test.__dict__:
            ans.update(test.results)
    return ans

# Example:
class TestSequenceFunctions(unittest.TestCase):

    def setUp(self):
        self.seq = range(10)

    @store_result
    def test_shuffle(self):
        # make sure the shuffled sequence does not lose any elements
        random.shuffle(self.seq)
        self.seq.sort()
        self.assertEqual(self.seq, range(10))
        # should raise an exception for an immutable sequence
        self.assertRaises(TypeError, random.shuffle, (1,2,3))
        return {1:2}

    @store_result
    def test_choice(self):
        element = random.choice(self.seq)
        self.assertTrue(element in self.seq)
        return {7:2}

    @store_result
    def test_sample(self):
        x = 799
        with self.assertRaises(ValueError):
            random.sample(self.seq, 20)
        for element in random.sample(self.seq, 5):
            self.assertTrue(element in self.seq)
        return {1:99999}


suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)

from pprint import pprint
pprint(suite_results(suite))

अंतिम पंक्ति उन मानों को प्रिंट करेगी जहां परीक्षण सफल हुआ और स्थानीय चर, इस स्थिति में x, जब यह विफल होता है:

{'test_choice': {'result': {7: 2}, 'success': True},
 'test_sample': {'locals': {'self': <__main__.TestSequenceFunctions testMethod=test_sample>,
                            'x': 799},
                 'success': False},
 'test_shuffle': {'result': {1: 2}, 'success': True}}

Har det gøy :-)


0

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

अस्वीकरण: मैंने अजगर की इकाई परीक्षण रूपरेखा के साथ यह कोशिश नहीं की है, लेकिन अन्य इकाई परीक्षण रूपरेखाओं के साथ है।



-1

@FC के उत्तर का विस्तार करते हुए, यह मेरे लिए बहुत अच्छा काम करता है:

class MyTest(unittest.TestCase):
    def messenger(self, message):
        try:
            self.assertEqual(1, 2, msg=message)
        except AssertionError as e:      
            print "\nMESSENGER OUTPUT: %s" % str(e),
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.