如何在Python中用nosetest / unittest声明输出?
我正在写下一个函数的testing:
def foo(): print 'hello world!'
所以当我想testing这个函数时,代码将会是这样的:
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参数运行noseteststesting崩溃。 如何用unittest或nose模块捕捉输出?
我使用这个上下文pipe理器来捕获输出。 它通过临时replacesys.stdout
最终使用与其他一些答案相同的技术。 我更喜欢上下文pipe理器,因为它把所有的簿记包装成一个单独的函数,所以我不必重写任何try-finally代码,我不必为此编写setup和teardown函数。
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
块的时候恢复的,所以我们可以在第一个函数中设置第二个捕获块,这是使用setup和teardown函数不可能的,在try-最后手动阻止。 当testing的目标是比较两个函数的相对结果而不是一些预先计算的值时,这种能力就派上用场了。
如果你真的想这样做,你可以在testing期间重新分配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
parameter passing给foo
函数。
def foo(out=sys.stdout): out.write("hello, world!")
那么testing就简单多了:
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
,这是通过buffer
标志提供的。 而且,这是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!')
您可以通过--buffer
命令行标志-b
,– --buffer
或unittest.main
选项来设置缓冲区。 相反的是通过--nocapture
旗帜 – --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)
我只是刚刚学习Python,发现自己正在努力解决与上面类似的问题,unit testing的输出方法。 我上面的foo模块的unit testing已经结束了,看起来像这样:
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')
很多这些答案都失败了,因为你不能from StringIO import StringIO
Python 3中的from StringIO import StringIO
中获取这个。下面是一个基于@ 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')
编写testing通常会向我们展示编写代码的更好方法。 与Shane的回答类似,我想提出另一种看待这个问题的方法。 你真的想断言你的程序输出了一个特定的string,或只是它构造了一个特定的string输出? 这变得更容易testing,因为我们可以假定Python print
语句正确地工作。
def foo_msg(): return 'hello world' def foo(): print foo_msg()
那么你的testing很简单:
def test_foo_msg(): assert 'hello world' == foo_msg()
当然,如果你真的需要testing你的程序的实际输出,那么可以随意忽略。 🙂
在python 3.5中,你可以使用StringIO()
和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!'
或者考虑使用pytest,它已经内置了支持stdout和stderr的支持。 看文档
根据Rob Kennedy的回答,我编写了一个基于类的上下文pipe理器来缓冲输出。
用法如下:
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()