[python] 파이썬에서 nosetest / unittest로 출력을 주장하는 방법은 무엇입니까?

다음과 같은 기능에 대한 테스트를 작성하고 있습니다.

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 매개 변수로 nosetest를 실행하면 테스트가 중단됩니다. unittest 또는 nose 모듈로 출력을 어떻게 잡을 수 있습니까?



답변

컨텍스트 관리자 를 사용하여 출력을 캡처합니다. 일시적으로 대체하여 궁극적으로 다른 답변 중 일부와 동일한 기술을 사용합니다 sys.stdout. 모든 부기를 하나의 함수로 래핑하기 때문에 컨텍스트 관리자를 선호하므로 try-finally 코드를 다시 작성할 필요가 없으며이를 위해 설정 및 해체 함수를 작성할 필요가 없습니다.

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블록 을 종료하면 원래 출력 상태가 복원 되므로 첫 번째 캡처 블록과 동일한 기능으로 두 번째 캡처 블록을 설정할 수 있습니다. 이는 설정 및 해체 기능을 사용할 수 없으며 try-finally를 작성할 때 말이 많아집니다. 수동으로 차단합니다. 이 기능은 테스트의 목표가 미리 계산 된 값보다는 서로 상대적인 두 함수의 결과를 비교하는 것이었을 때 유용했습니다.


답변

정말로이 작업을 수행하려면 테스트 기간 동안 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!'


답변

버전 2.7부터는 더 이상 재 할당 할 필요가 없으며 sys.stdout이는 bufferflag를 통해 제공됩니다 . 또한 nosetest의 기본 동작입니다.

다음은 버퍼링되지 않은 컨텍스트에서 실패한 샘플입니다.

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옵션. 그 반대는 nosetestflag를 통해 이루어집니다 --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)


답변

from StringIO import StringIOPython 3에서 할 수 없기 때문에 이러한 답변 중 상당수가 실패했습니다. 여기 @naxa의 의견과 Python Cookbook을 기반으로 한 최소 작업 스 니펫이 있습니다.

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


답변

나는 단지 파이썬을 배우는 중이고 출력이있는 메서드에 대한 단위 테스트로 위의 문제와 유사한 문제로 어려움을 겪고 있음을 발견했습니다. 위의 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')


답변

테스트를 작성하는 것은 종종 코드를 작성하는 더 나은 방법을 보여줍니다. Shane의 대답과 마찬가지로 이것을 보는 또 다른 방법을 제안하고 싶습니다. 프로그램 이 특정 문자열 을 출력 했거나 출력을 위해 특정 문자열을 구성했다고 정말로 주장 하시겠습니까? Python print문이 제대로 작동 한다고 가정 할 수 있기 때문에 테스트하기가 더 쉬워집니다 .

def foo_msg():
    return 'hello world'

def foo():
    print foo_msg()

그렇다면 테스트는 매우 간단합니다.

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

물론, 프로그램의 실제 출력을 테스트해야 할 필요가 있다면 무시해도됩니다. 🙂