अजगर में nosetest / unittest के साथ आउटपुट कैसे दर्ज करें?


114

मैं अगले एक जैसे फ़ंक्शन के लिए परीक्षण लिख रहा हूं:

def foo():
    print 'hello world!'

इसलिए जब मैं इस फ़ंक्शन का परीक्षण करना चाहता हूं तो कोड इस तरह होगा:

import sys
from foomodule import foo
def test_foo():
    foo()
    output = sys.stdout.getline().strip() # because stdout is an StringIO instance
    assert output == 'hello world!'

लेकिन अगर मैं -s पैरामीटर के साथ nosetests चलाता हूं तो परीक्षण क्रैश हो जाता है। मैं आउटपुट को सबसे अच्छे या नाक मॉड्यूल से कैसे पकड़ सकता हूं?


जवाबों:


124

मैं आउटपुट को पकड़ने के लिए इस संदर्भ प्रबंधक का उपयोग करता हूं । यह अंततः उसी तकनीक का उपयोग करता है जो अस्थायी रूप से प्रतिस्थापित करके कुछ अन्य उत्तरों के रूप में होती है sys.stdout। मैं संदर्भ प्रबंधक को पसंद करता हूं क्योंकि यह सभी बहीखाते को एक ही फ़ंक्शन में लपेटता है, इसलिए मुझे किसी भी कोशिश-अंत कोड को फिर से लिखना नहीं पड़ता है, और मुझे इसके लिए सेटअप और फाड़ कार्यों को लिखने की ज़रूरत नहीं है।

import sys
from contextlib import contextmanager
from StringIO import StringIO

@contextmanager
def captured_output():
    new_out, new_err = StringIO(), StringIO()
    old_out, old_err = sys.stdout, sys.stderr
    try:
        sys.stdout, sys.stderr = new_out, new_err
        yield sys.stdout, sys.stderr
    finally:
        sys.stdout, sys.stderr = old_out, old_err

इसे इस तरह उपयोग करें:

with captured_output() as (out, err):
    foo()
# This can go inside or outside the `with` block
output = out.getvalue().strip()
self.assertEqual(output, 'hello world!')

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


यह pep8radius में मेरे लिए वास्तव में अच्छी तरह से काम किया है । हाल ही में, हालांकि, मैंने इसे फिर से उपयोग किया था और TypeError: unicode argument expected, got 'str'प्रिंट करते समय निम्न त्रुटि मिलती है (प्रिंट करने के लिए पारित प्रकार (str / यूनिकोड) अप्रासंगिक है)।
एंडी हेडन

9
हम्म यह हो सकता है कि अजगर 2 में हम चाहते हैं from io import BytesIO as StringIOऔर अजगर 3 में बस from io import StringIO। मुझे लगता है कि मेरे परीक्षणों में समस्या को ठीक करने के लिए लिया गया।
एंडी हेडन

4
ओह, बस खत्म करने के लिए, इतने सारे संदेशों के लिए माफी। इसे खोजने वाले लोगों के लिए स्पष्ट करने के लिए: python3 io.StringIO, python 2 का उपयोग StringIO.StringIO करें! एक बार फिर धन्यवाद!
एंडी हेडन

सभी उदाहरण यहाँ से लौटे strip()पर क्यों बुला रहे हैं ? unicodeStringIO.getvalue()
पालिमोंडो

1
नहीं, @ वेदरन। यह उस नाम को पुन: स्थापित करने पर निर्भर करता है जो संबंधित है sys। अपने आयात विवरण के साथ, आप एक स्थानीय वैरिएबल बना रहे हैं, जिसका नाम मूल्य की stderrएक प्रति है sys.stderr। एक में परिवर्तन दूसरे में परिलक्षित नहीं होता है।
रोब कैनेडी

60

यदि आप वास्तव में ऐसा करना चाहते हैं, तो आप परीक्षण की अवधि के लिए sys.stdout को पुन: असाइन कर सकते हैं।

def test_foo():
    import sys
    from foomodule import foo
    from StringIO import StringIO

    saved_stdout = sys.stdout
    try:
        out = StringIO()
        sys.stdout = out
        foo()
        output = out.getvalue().strip()
        assert output == 'hello world!'
    finally:
        sys.stdout = saved_stdout

यदि मैं इस कोड को लिख रहा था, हालांकि, मैं फ़ंक्शन के outलिए एक वैकल्पिक पैरामीटर पास करना पसंद करूंगा foo

def foo(out=sys.stdout):
    out.write("hello, world!")

फिर परीक्षण बहुत सरल है:

def test_foo():
    from foomodule import foo
    from StringIO import StringIO

    out = StringIO()
    foo(out=out)
    output = out.getvalue().strip()
    assert output == 'hello world!'

11
नोट: अजगर 3.x के तहत StringIOवर्ग अब ioमॉड्यूल से आयात किया जाना चाहिए । from io import StringIOअजगर 2.6+ में काम करता है।
ब्रायन पी

2
यदि आप from io import StringIOअजगर 2 में उपयोग करते हैं , तो आपको एक TypeError: unicode argument expected, got 'str'मुद्रण मिलता है ।
मत्तीस

9
त्वरित नोट: अजगर 3.4 में, आप इसे इस तरह से करने के लिए Referencelib.redirect_stdout संदर्भ प्रबंधक का उपयोग कर सकते हैं जो अपवाद सुरक्षित है:with redirect_stdout(out):
Lucretiel

2
आपको करने की आवश्यकता नहीं है saved_stdout = sys.stdout, आपके पास हमेशा इस पर एक जादू रेफरी होता है sys.__stdout__, उदाहरण के लिए, आपको केवल sys.stdout = sys.__stdout__अपने सफाई में आवश्यकता होती है।
थोरसुमोनर

@ThorSummoner धन्यवाद, इसने मेरे कुछ परीक्षणों को सरल बना दिया ... स्कूबा के लिए जो मैंने देखा कि आपने तारांकित किया है .... छोटी सी दुनिया!
जोनाथन रेनहार्ट

48

संस्करण 2.7 के बाद से, आपको पुन: असाइन करने की आवश्यकता नहीं है sys.stdout, यह bufferध्वज के माध्यम से प्रदान किया जाता है । इसके अलावा, यह नाओसेटेस्ट का डिफ़ॉल्ट व्यवहार है।

यहाँ गैर बफर संदर्भ में एक नमूना विफल हो रहा है:

import sys
import unittest

def foo():
    print 'hello world!'

class Case(unittest.TestCase):
    def test_foo(self):
        foo()
        if not hasattr(sys.stdout, "getvalue"):
            self.fail("need to run in buffered mode")
        output = sys.stdout.getvalue().strip() # because stdout is an StringIO instance
        self.assertEquals(output,'hello world!')

आप unit2कमांड लाइन ध्वज -b, --bufferया unittest.mainविकल्पों के माध्यम से बफर सेट कर सकते हैं। nosetestझंडा के माध्यम से विपरीत हासिल किया जाता है --nocapture

if __name__=="__main__":   
    assert not hasattr(sys.stdout, "getvalue")
    unittest.main(module=__name__, buffer=True, exit=False)
    #.
    #----------------------------------------------------------------------
    #Ran 1 test in 0.000s
    #
    #OK
    assert not hasattr(sys.stdout, "getvalue")

    unittest.main(module=__name__, buffer=False)
    #hello world!
    #F
    #======================================================================
    #FAIL: test_foo (__main__.Case)
    #----------------------------------------------------------------------
    #Traceback (most recent call last):
    #  File "test_stdout.py", line 15, in test_foo
    #    self.fail("need to run in buffered mode")
    #AssertionError: need to run in buffered mode
    #
    #----------------------------------------------------------------------
    #Ran 1 test in 0.002s
    #
    #FAILED (failures=1)

ध्यान दें कि यह साथ बातचीत करता है --nocapture; विशेष रूप से, यदि यह ध्वज सेट है, तो बफ़र्ड मोड अक्षम हो जाएगा। तो आपके पास टर्मिनल पर आउटपुट को देखने में सक्षम होने , या यह परीक्षण करने में सक्षम होने का विकल्प है कि आउटपुट अपेक्षित है।
१ .:

1
क्या प्रत्येक परीक्षण के लिए इसे चालू और बंद करना संभव है, क्योंकि यह ipdb.set_trace () जैसी किसी चीज़ का उपयोग करते समय डिबगिंग को बहुत मुश्किल बनाता है?
लेक्वेर्विग

33

इनमें से बहुत सारे उत्तर मेरे लिए विफल रहे क्योंकि आप from StringIO import StringIOपायथन 3 में नहीं हो सकते । यहाँ @ नक्सा की टिप्पणी और पायथन कुकबुक के आधार पर एक न्यूनतम काम करने वाला स्निपेट है।

from io import StringIO
from unittest.mock import patch

with patch('sys.stdout', new=StringIO()) as fakeOutput:
    print('hello world')
    self.assertEqual(fakeOutput.getvalue().strip(), 'hello world')

3
मैं पायथन 3 के लिए यह एक प्यार करता हूं, यह साफ है!
सिलहारे

1
इस पृष्ठ पर यह एकमात्र समाधान था जिसने मेरे लिए काम किया! धन्यवाद।
जस्टिन एइस्टर

24

अजगर 3.5 में आप उपयोग कर सकते हैं contextlib.redirect_stdout()और StringIO()। यहां आपके कोड में संशोधन किया गया है

import contextlib
from io import StringIO
from foomodule import foo

def test_foo():
    temp_stdout = StringIO()
    with contextlib.redirect_stdout(temp_stdout):
        foo()
    output = temp_stdout.getvalue().strip()
    assert output == 'hello world!'

बहुत बढ़िया जवाब! प्रलेखन के अनुसार यह पायथन 3.4 में जोड़ा गया था।
हाइपरक्यूब

यह redirect_stdout के लिए 3.4 और redirect_stderr के लिए 3.5 है। शायद यहीं से भ्रम पैदा हुआ!
rbennell

redirect_stdout()और redirect_stderr()उनके इनपुट तर्क को वापस करें। तो, with contextlib.redirect_stdout(StringIO()) as temp_stdout:आप सभी को एक पंक्ति में देता है। 3.7.1 के साथ परीक्षण किया गया।
एड्रियन डब्ल्यू

17

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

import sys
import unittest
from foo import foo
from StringIO import StringIO

class FooTest (unittest.TestCase):
    def setUp(self):
        self.held, sys.stdout = sys.stdout, StringIO()

    def test_foo(self):
        foo()
        self.assertEqual(sys.stdout.getvalue(),'hello world!\n')

5
आप के sys.stdout.getvalue().strip()साथ तुलना और धोखा नहीं करना चाहते हो सकता है \n:)
सिल्वियू

स्ट्रिंग मॉड्यूल को हटा दिया गया है। इसके बजायfrom io import StringIO
एडवरिक

10

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

def foo_msg():
    return 'hello world'

def foo():
    print foo_msg()

तब आपका परीक्षण बहुत सरल है:

def test_foo_msg():
    assert 'hello world' == foo_msg()

बेशक, अगर आपको वास्तव में अपने प्रोग्राम के वास्तविक आउटपुट का परीक्षण करने की आवश्यकता है, तो अवहेलना करने के लिए स्वतंत्र महसूस करें। :)


1
लेकिन इस मामले में फू का परीक्षण नहीं किया जाएगा ... शायद यह एक समस्या है
पेड्रो वालेंसिया

5
एक परीक्षण शुद्धतावादी के दृष्टिकोण से, शायद यह एक समस्या है। एक व्यावहारिक दृष्टिकोण से, अगर foo()कुछ भी नहीं करता है , लेकिन प्रिंट स्टेटमेंट को कॉल करें, तो शायद यह कोई समस्या नहीं है।
एलिसन आर।

5

रोब कैनेडी के जवाब के आधार पर, मैंने आउटपुट को बफर करने के लिए संदर्भ प्रबंधक का एक वर्ग-आधारित संस्करण लिखा।

उपयोग की तरह है:

with OutputBuffer() as bf:
    print('hello world')
assert bf.out == 'hello world\n'

यहाँ कार्यान्वयन है:

from io import StringIO
import sys


class OutputBuffer(object):

    def __init__(self):
        self.stdout = StringIO()
        self.stderr = StringIO()

    def __enter__(self):
        self.original_stdout, self.original_stderr = sys.stdout, sys.stderr
        sys.stdout, sys.stderr = self.stdout, self.stderr
        return self

    def __exit__(self, exception_type, exception, traceback):
        sys.stdout, sys.stderr = self.original_stdout, self.original_stderr

    @property
    def out(self):
        return self.stdout.getvalue()

    @property
    def err(self):
        return self.stderr.getvalue()

2

या उपयोग करने पर विचार करें pytest, इसमें स्टडआउट और स्टैडर को शामिल करने के लिए अंतर्निहित समर्थन है। डॉक्स देखें

def test_myoutput(capsys): # or use "capfd" for fd-level
    print("hello")
    captured = capsys.readouterr()
    assert captured.out == "hello\n"
    print("next")
    captured = capsys.readouterr()
    assert captured.out == "next\n"

अच्छा था। क्या आप न्यूनतम उदाहरण शामिल कर सकते हैं क्योंकि लिंक गायब हो सकते हैं और सामग्री बदल सकती है?
कोभोज

2

दोनों n611x007 और नाउमेनन ने पहले से ही उपयोग करने का सुझाव दिया था unittest.mock, लेकिन यह उत्तर एक्यूमेनस को यह दिखाने के लिए प्रेरित करता है कि आप कैसे आसानी से unittest.TestCaseएक नकली के साथ बातचीत करने के तरीकों को लपेट सकते हैं stdout

import io
import unittest
import unittest.mock

msg = "Hello World!"


# function we will be testing
def foo():
    print(msg, end="")


# create a decorator which wraps a TestCase method and pass it a mocked
# stdout object
mock_stdout = unittest.mock.patch('sys.stdout', new_callable=io.StringIO)


class MyTests(unittest.TestCase):

    @mock_stdout
    def test_foo(self, stdout):
        # run the function whose output we want to test
        foo()
        # get its output from the mocked stdout
        actual = stdout.getvalue()
        expected = msg
        self.assertEqual(actual, expected)

0

इस धागे में सभी भयानक उत्तरों का निर्माण, यह है कि मैंने इसे कैसे हल किया। मैं इसे यथासंभव स्टॉक में रखना चाहता था। मैं इकाई परीक्षण प्रणाली का उपयोग कर संवर्धित setUp()कब्जा करने के लिए sys.stdoutऔर sys.stderr, नई ज़ोर एपीआई एक उम्मीद मूल्य के खिलाफ कब्जा कर लिया मूल्यों की जाँच और फिर बहाल करने के लिए जोड़ा sys.stdoutऔर sys.stderrपर tearDown(). I did this to keep a similar unit test API as the built-inunittest API while still being able to unit test values printed tosys.stdout orsys.stderr`।

import io
import sys
import unittest


class TestStdout(unittest.TestCase):

    # before each test, capture the sys.stdout and sys.stderr
    def setUp(self):
        self.test_out = io.StringIO()
        self.test_err = io.StringIO()
        self.original_output = sys.stdout
        self.original_err = sys.stderr
        sys.stdout = self.test_out
        sys.stderr = self.test_err

    # restore sys.stdout and sys.stderr after each test
    def tearDown(self):
        sys.stdout = self.original_output
        sys.stderr = self.original_err

    # assert that sys.stdout would be equal to expected value
    def assertStdoutEquals(self, value):
        self.assertEqual(self.test_out.getvalue().strip(), value)

    # assert that sys.stdout would not be equal to expected value
    def assertStdoutNotEquals(self, value):
        self.assertNotEqual(self.test_out.getvalue().strip(), value)

    # assert that sys.stderr would be equal to expected value
    def assertStderrEquals(self, value):
        self.assertEqual(self.test_err.getvalue().strip(), value)

    # assert that sys.stderr would not be equal to expected value
    def assertStderrNotEquals(self, value):
        self.assertNotEqual(self.test_err.getvalue().strip(), value)

    # example of unit test that can capture the printed output
    def test_print_good(self):
        print("------")

        # use assertStdoutEquals(value) to test if your
        # printed value matches your expected `value`
        self.assertStdoutEquals("------")

    # fails the test, expected different from actual!
    def test_print_bad(self):
        print("@=@=")
        self.assertStdoutEquals("@-@-")


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

जब यूनिट परीक्षण चलाया जाता है, तो आउटपुट है:

$ python3 -m unittest -v tests/print_test.py
test_print_bad (tests.print_test.TestStdout) ... FAIL
test_print_good (tests.print_test.TestStdout) ... ok

======================================================================
FAIL: test_print_bad (tests.print_test.TestStdout)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tests/print_test.py", line 51, in test_print_bad
    self.assertStdoutEquals("@-@-")
  File "/tests/print_test.py", line 24, in assertStdoutEquals
    self.assertEqual(self.test_out.getvalue().strip(), value)
AssertionError: '@=@=' != '@-@-'
- @=@=
+ @-@-


----------------------------------------------------------------------
Ran 2 tests in 0.001s

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