পাইথনে নাকটেস্ট / ইউনিটেস্টের সাথে আউটপুট কীভাবে যুক্ত করা যায়?


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 প্যারামিটার দিয়ে নস্টিস্ট চালালে পরীক্ষা ক্র্যাশ হয়। ইউনিটেস্ট বা নাকের মডিউল দিয়ে আমি কীভাবে আউটপুট ধরতে পারি?


উত্তর:


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


এটি আমার জন্য পেপ 8 ফ্রিডিয়াসে খুব ভাল কাজ করেছে । সম্প্রতি যাইহোক, আমি আবার এটি ব্যবহার করেছি এবং মুদ্রণের সময় নিম্নলিখিত ত্রুটিটি পেয়েছি TypeError: unicode argument expected, got 'str'(প্রিন্টে প্রেরণ করা প্রবন্ধটি (স্ট্রিং / ইউনিকোড) অপ্রাসঙ্গিক)।
অ্যান্ডি হেডেন

9
হুমম এটি হতে পারে পাইথন 2 এ আমরা চাই from io import BytesIO as StringIOএবং পাইথন 3 তে ঠিক from io import StringIO। আমার মনে হয় আমার পরীক্ষাগুলিতে সমস্যাটি সমাধান করার জন্য মনে হয়েছে।
অ্যান্ডি হেডেন

4
ওওপ, ঠিক শেষ করার জন্য, অনেক বার্তার জন্য ক্ষমা চাই। লোকেরা এটির সন্ধানের জন্য কেবল স্পষ্ট করে বলতে: পাইথন 3 io.StringIO ব্যবহার করে, পাইথন 2 স্ট্রিংআইও.স্ট্রিংআইও ব্যবহার করে! আবার ধন্যবাদ!
অ্যান্ডি হেডেন

কেন এখানে সমস্ত উদাহরণগুলি থেকে ফিরে আসার আহ্বান জানানো strip()হচ্ছে ? unicodeStringIO.getvalue()
Palimondo

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

আমি যদি এই কোডটি লিখতে থাকি তবে আমি ফাংশনে একটি al 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
দ্রষ্টব্য: পাইথন ৩.x এর অধীনে StringIOক্লাসটি এখন ioমডিউল থেকে আমদানি করতে হবে । from io import StringIOপাইথন ২.6++ এ কাজ করে।
ব্রায়ান পি

2
আপনি from io import StringIOঅজগর 2 ব্যবহার করেন, আপনি TypeError: unicode argument expected, got 'str'মুদ্রণ সময় পাবেন।
matiasg

9
তাত্ক্ষণিক দ্রষ্টব্য: অজগর ৩.৪-তে, আপনি ব্যতিক্রমী সুরক্ষিত উপায়ে এটি করতে কনটেক্সটলিব.ড্রেইট_স্টডআউট প্রসঙ্গ ম্যানেজারটি ব্যবহার করতে পারেন :with redirect_stdout(out):
লুক্রটিয়েল

2
আপনাকে করার দরকার নেই saved_stdout = sys.stdout, আপনার কাছে সর্বদা এটির জন্য একটি যাদু রেফ রয়েছে sys.__stdout__, উদাহরণস্বরূপ, আপনার কেবল sys.stdout = sys.__stdout__আপনার ক্লিনআপে প্রয়োজন ।
থারস্মমনার

@ThorSummoner ধন্যবাদ, এই মাত্র আমার পরীক্ষার কিছু সরলীকৃত ... এর জন্য স্কুবা যা আমি দেখতে আপনার তারকাচিহ্নিত .... ছোট দুনিয়া!
জোনাথন রাইনহার্ট

48

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

1
প্রতিটি পরীক্ষার জন্য কি এটি চালু এবং বন্ধ করা সম্ভব, কারণ ipdb.set_trace () এর মতো কিছু ব্যবহার করার সময় এটি ডিবাগিংকে খুব কঠিন করে তোলে?
Lqueryvg

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 এর জন্য এটি পছন্দ করি, এটি পরিষ্কার!
সিলেরে 16 ই

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!'

দুর্দান্ত উত্তর! ডকুমেন্টেশন অনুসারে এটি পাইথন ৩.৪-এ যুক্ত করা হয়েছিল।
হাইপারকিউব

এটি redirect_stdout এর জন্য 3.4 এবং redirect_stderr এর জন্য 3.5। সেখানেই হয়তো বিভ্রান্তি দেখা দিয়েছে!
rbennell

redirect_stdout()এবং redirect_stderr()তাদের ইনপুট আর্গুমেন্ট ফিরে। সুতরাং, with contextlib.redirect_stdout(StringIO()) as temp_stdout:আপনাকে সমস্ত এক লাইনে দেয়। ৩. 3..১ নিয়ে পরীক্ষা করা হয়েছে।
অ্যাড্রিয়ান ডব্লিউ

17

আমি কেবল পাইথন শিখছি এবং আউটপুট সহ পদ্ধতির জন্য ইউনিট পরীক্ষার সাথে উপরের সমস্যার মতো একই সমস্যার সাথে নিজেকে লড়াই করতে দেখলাম। উপরের foo মডিউলের জন্য আমার পাসিং ইউনিট পরীক্ষাটি দেখতে দেখতে শেষ হয়েছে:

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, এটি stdout এবং stderr জোর দেওয়ার জন্য অন্তর্নির্মিত সমর্থন আছে। দস্তাবেজগুলি দেখুন

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, কিন্তু এই উত্তর আত্তীকরণ Acumenus এর দেখানোর জন্য কিভাবে আপনি সহজেই মোড়ানো পারেন 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নতুন জাহির API গুলি একটি প্রত্যাশিত মান বিরুদ্ধে বন্দী মান চেক করুন ও তারপরে পুনঃস্থাপন যোগ 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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.