如何在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 参数运行鼻子测试,测试就会崩溃。如何使用unittest或nose模块捕获输出?
I'm writing tests for a function like next one:
def foo():
print 'hello world!'
So when I want to test this function the code will be like this:
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!'
But if I run nosetests with -s parameter the test crashes. How can I catch the output with unittest or nose module?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
建立在这个线程中所有很棒的答案的基础上,这就是我解决它的方法。我想尽可能保留它的库存。我使用
setUp()
增强了单元测试机制来捕获sys.stdout
和sys.stderr
,添加了新的断言 API 来检查捕获的值与预期值相反,然后在tearDown() 上恢复
unittestsys.stdout
和sys.stderr
。我这样做是为了保留与内置API 类似的单元测试 API,同时仍然能够将单元测试值打印到
sys.stdout或
sys .stderr`。运行单元测试时,输出为:
Building on all the awesome answers in this thread, this is how I solved it. I wanted to keep it as stock as possible. I augmented the unit test mechanism using
setUp()
to capturesys.stdout
andsys.stderr
, added new assert APIs to check the captured values against an expected value and then restoresys.stdout
andsys.stderr
upontearDown(). I did this to keep a similar unit test API as the built-in
unittestAPI while still being able to unit test values printed to
sys.stdoutor
sys.stderr`.When the unit test is run, the output is:
我喜欢索伦斯对问题和示例代码的简单[答案][1],特别是因为我不熟悉补丁/模拟等新功能。 sorens 没有建议一种方法来使示例代码的 TestStdIO 类的自定义断言方法可重用,而无需诉诸剪切/粘贴,因此我采取了使 TestStdIO 类的自定义断言方法>TestStdIO 在其自己的模块中定义的“mixin”类(以下示例中的teststdoutmethods.py)。由于 TestStdIO 中使用的通常 unittest.TestCase 提供的断言方法引用也将在测试用例类中可用,因此我从他的示例代码中删除了 import unittest 行,并且还从类声明中的 unittest.TestCase 派生出 TestStdIO,即,
否则 TestStdIO 的代码与 sorens 的版本相同,最后没有两个示例用法。
我在第 1 章的一个基本示例文本游戏中的一个类的一些简单的单元测试测试用例中使用了 TestStdIO 的这个 mixin 类版本。 Kinsley 和 McGugan 的 使用 PyGame 开始 Python 游戏编程 的第 2 部分,例如
测试用例(成功和失败)在 Python 3.7 中运行。请注意,sorens 的技术捕获了 setup() 和teardown() 调用之间的所有标准输出输出,因此我将它们放置在将生成我想要检查的特定输出的特定操作周围。我认为我的 mixin 方法是 sorens 想要普遍重用的方法,但我想知道是否有人有不同的建议。谢谢。
[1]:https://stackoverflow.com/a/62429695/7386731
I like sorens' straightforward [Answer][1] to the question and sample code, particularly since I'm not familiar with newer features like patch/mock. sorens didn't suggest a way to make the custom assertion methods of the example code's TestStdIO class reusable without resorting to cut/paste, so I took the approach of making TestStdIO a "mixin" class defined in its own module (teststdoutmethods.py in the following example). Since the usual unittest.TestCase-provided assert method references used in TestStdIO will also be available in the test case class, I removed the import unittest line from his sample code and also the derivation of TestStdIO from unittest.TestCase in the class declaration, i.e.,
Otherwise the code of TestStdIO is as sorens' version sans the two example usages at the end.
I used this mixin class version of TestStdIO in some simple unittest test cases of a class in one of the basic example text games in Ch. 2 of Kinsley and McGugan's Beginning Python Game Programming with PyGame, e.g.
The test cases (both successes and ginned failures) worked in Python 3.7. Note that sorens' technique captures all of the stdout output between the setup() and teardown() calls, so I placed these around the specific actions that would generate the specific output I wanted to check. I presume my mixin approach is what sorens would have intended for general reuse, but I'd like to know if anyone has a different recommendation. Thx.
[1]: https://stackoverflow.com/a/62429695/7386731
我使用这个 上下文管理器 来捕获输出。它最终使用与其他一些答案相同的技术,暂时替换 sys.stdout 。我更喜欢上下文管理器,因为它将所有簿记包装到一个函数中,因此我不必重新编写任何 try-finally 代码,也不必为此编写设置和拆卸函数。
像这样使用它:
此外,由于退出
with
块时会恢复原始输出状态,因此我们可以在与第一个捕获块相同的函数中设置第二个捕获块,这是不可能的设置和拆卸函数,并且在手动编写 try-finally 块时变得冗长。当测试的目标是比较两个函数的结果而不是与某个预先计算的值时,这种能力就派上用场了。I use this context manager to capture output. It ultimately uses the same technique as some of the other answers by temporarily replacing
sys.stdout
. I prefer the context manager because it wraps all the bookkeeping into a single function, so I don't have to re-write any try-finally code, and I don't have to write setup and teardown functions just for this.Use it like this:
Furthermore, since the original output state is restored upon exiting the
with
block, we can set up a second capture block in the same function as the first one, which isn't possible using setup and teardown functions, and gets wordy when writing try-finally blocks manually. That ability came in handy when the goal of a test was to compare the results of two functions relative to each other rather than to some precomputed value.如果您确实想这样做,可以在测试期间重新分配 sys.stdout。
但是,如果我编写此代码,我更愿意将可选的
out
参数传递给foo
函数。那么测试就简单多了:
If you really want to do this, you can reassign sys.stdout for the duration of the test.
If I were writing this code, however, I would prefer to pass an optional
out
parameter to thefoo
function.Then the test is much simpler:
从版本 2.7 开始,您不再需要重新分配
sys.stdout
,这是通过缓冲区
标志。而且,这是nosetest的默认行为。以下是在非缓冲上下文中失败的示例:
您可以通过
unit2
命令行标志-b
、--buffer
或在中设置缓冲区>unittest.main
选项。相反的情况是通过
nosetest
标志--nocapture
实现的。Since version 2.7, you do not need anymore to reassign
sys.stdout
, this is provided throughbuffer
flag. Moreover, it is the default behavior of nosetest.Here is a sample failing in non buffered context:
You can set buffer through
unit2
command line flag-b
,--buffer
or inunittest.main
options.The opposite is achieved through
nosetest
flag--nocapture
.其中很多答案对我来说都失败了,因为你不能在 Python 3 中
from StringIO import StringIO
。这是基于 @naxa 的评论和 Python Cookbook 的最小工作片段。A lot of these answers failed for me because you can't
from StringIO import StringIO
in Python 3. Here's a minimum working snippet based on @naxa's comment and the Python Cookbook.在 python 3.5 中,您可以使用 contextlib.redirect_stdout() 和 StringIO()。这是对您的代码的修改
In python 3.5 you can use
contextlib.redirect_stdout()
andStringIO()
. Here's the modification to your code我刚刚学习Python,发现自己在对带有输出的方法进行单元测试时遇到了与上述问题类似的问题。我通过上面的 foo 模块的单元测试最终看起来像这样:
I'm only just learning Python and found myself struggling with a similar problem to the one above with unit tests for methods with output. My passing unit test for foo module above has ended up looking like this:
编写测试通常会向我们展示更好的编写代码的方法。与谢恩的回答类似,我想提出另一种看待这个问题的方式。您真的想断言您的程序输出了某个字符串,还是只是构建了某个字符串用于输出?这变得更容易测试,因为我们可以假设 Python
print
语句正确地完成了它的工作。那么你的测试就非常简单了:
当然,如果你确实需要测试你的程序的实际输出,那么就可以忽略。 :)
Writing tests often shows us a better way to write our code. Similar to Shane's answer, I'd like to suggest yet another way of looking at this. Do you really want to assert that your program outputted a certain string, or just that it constructed a certain string for output? This becomes easier to test, since we can probably assume that the Python
print
statement does its job correctly.Then your test is very simple:
Of course, if you really have a need to test your program's actual output, then feel free to disregard. :)
n611x007 和 本体已经建议使用
unittest.mock
,但这个答案适应Acumenus 展示了如何轻松包装unittest.TestCase
方法以与模拟的stdout
交互。Both n611x007 and Noumenon already suggested using
unittest.mock
, but this answer adapts Acumenus's to show how you can easily wrapunittest.TestCase
methods to interact with a mockedstdout
.根据 Rob Kennedy 的回答,我编写了一个基于类的上下文管理器版本来缓冲输出。
用法如下:
这是实现:
Based on Rob Kennedy's answer, I wrote a class-based version of the context manager to buffer the output.
Usage is like:
Here's the implementation:
或者考虑使用 pytest,它内置了对断言 stdout 和 stderr 的支持。请参阅文档
Or consider using
pytest
, it has built-in support for asserting stdout and stderr. See docsUnittest 现在附带了一个上下文管理器(Python 3.7,但也可能是早期版本)。你可以这样做:
然后在你的单元测试中:
取自 https:// /pythonin1minute.com/how-to-test-logging-in-python/
Unittest ships with a context manager now (Python 3.7, but maybe earlier versions as well). You can just do this:
Then in your unit test:
Taken from https://pythonin1minute.com/how-to-test-logging-in-python/