在python中,如何对没有返回值的函数进行单元测试?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15940389/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In python, how to do unit test on a function without return value?
提问by Yarkee
I am a pythoner. At these days I am driving myself to do a more complete unit test on some core module in my project. Since we always do unit test with methods 'assertEqual', 'assertTrue' and so on, these methods all require a return value from the function being tested, I am wondering how to do a plain unit test on some function without a return value.
我是蟒蛇。这些天,我正在推动自己对项目中的某些核心模块进行更完整的单元测试。由于我们总是使用“assertEqual”、“assertTrue”等方法进行单元测试,因此这些方法都需要被测试函数的返回值,我想知道如何在没有返回值的情况下对某个函数进行简单的单元测试。
I would like to show a tiny example here, how to test function def foo(self, msg) in HelloTest?
我想在这里展示一个小例子,如何在 HelloTest 中测试函数 def foo(self, msg)?
class HelloTest(object):
def foo(self, msg):
MSG = msg.upper()
self.bar(MSG)
def bar(self, MSG):
print MSG
采纳答案by Jordan Dea-Mattson
In this particular case, I would mock print, then then use the mock in my assertion.
在这种特殊情况下,我会模拟打印,然后在我的断言中使用模拟。
In Python, you will use the Mock packageto mock.
在 Python 中,您将使用Mock 包进行模拟。
回答by Brendan Long
In Python 3, you can tell printwhere to print to:
在 Python 3 中,你可以告诉print打印到哪里:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
打印(*对象,sep='',end='\n',文件=sys.stdout,flush=False)
So add an optional argument:
所以添加一个可选参数:
def bar(self, MSG, file=sys.stdout):
print(MSG, file=file)
In normal usage, it will print to stdout, but for unit tests you can pass your own file.
在正常使用中,它会打印到标准输出,但对于单元测试,您可以传递自己的文件。
In Python 2 it's a bit messier, but you can redirect stdout to a StringIO buffer:
在 Python 2 中,它有点混乱,但您可以将 stdout 重定向到 StringIO 缓冲区:
import StringIO
import sys
out = StringIO.StringIO()
sys.stdout = out
# run unit tests
sys.stdout = sys.__stdout__
# check the contents of `out`
回答by Zangetsu
your code can be as given below which does same task as above:
您的代码可以如下所示,执行与上述相同的任务:
class HelloTest(object):
def foo(self, msg):
self.msg = msg.upper()
self.bar()
def bar(self):
print self.msg
Unit test is :
单元测试是:
from hello import HelloTest
import unittest
class TestFoo(unittest.TestCase):
def test_foo_case(self):
msg = "test"
ob = HelloTest()
ob.foo(msg)
expected = "TEST"
self.assertEqual(ob.msg, expected)
if __name__ == '__main__':
unittest.main(exit=False)
回答by aychedee
As another answer mentioned, you can use the Python mock library to make assertions about calls to functions/methods
正如提到的另一个答案,您可以使用 Python 模拟库对函数/方法的调用进行断言
from mock import patch
from my_module import HelloTest
import unittest
class TestFoo(unittest.TestCase):
@patch('hello.HelloTest.bar')
def test_foo_case(self, mock_bar):
ht = HelloTest()
ht.foo("some string")
self.assertTrue(mock_bar.called)
self.assertEqual(mock_bar.call_args[0][0], "SOME STRING")
This patches out the barmethod on HelloTest and replaces it with a mock object that records calls against it.
这修补了barHelloTest 上的方法,并将其替换为记录对其调用的模拟对象。
Mocking is a bit of a rabbit hole. Only do it when you absolutely have to because it does make your tests brittle. You'll never notice an API change for a mocked object for instance.
嘲笑有点像兔子洞。仅在绝对必要时才这样做,因为它确实会使您的测试变得脆弱。例如,您永远不会注意到模拟对象的 API 更改。
回答by Yarkee
Thanks to @Jordan 's introduction, I code this and think it is a workable unit test for HelloTest.foo
感谢@Jordan 的介绍,我对此进行了编码并认为它是 HelloTest.foo 的可行单元测试
from mock import Mock
import unittest
class HelloTestTestCase(unittest.TestCase):
def setUp(self):
self.hello_test = HelloTest()
def tearDown(self):
pass
def test_foo(self):
msg = 'hello'
expected_bar_arg = 'HELLO'
self.hello_test.bar = Mock()
self.hello_test.foo(msg)
self.hello_test.bar.assert_called_once_with(expected_bar_arg)
if __name__ == '__main__':
unittest.main()
回答by JonnyJD
I don't quite understand why everybody wants to check that foo calls bar.
我不太明白为什么每个人都想检查 foo 是否调用了 bar。
Foo has some functionality and this functionality needs to be tested. If foo is using bar to do this should not be of my concern.
Foo 有一些功能,这个功能需要测试。如果 foo 使用 bar 来做这件事,我不应该担心。
The desired result is that after foo(msg)is called, is that msg.upper()is sent to stdout.
期望的结果是在foo(msg)被调用之后,msg.upper()被发送到标准输出。
You can redirect stdout to a string bufferand check if the content of this string buffer matches what you expect.
您可以将 stdout 重定向到字符串缓冲区并检查此字符串缓冲区的内容是否与您期望的相符。
Example:
例子:
import sys
import unittest
from io import TextIOWrapper, BytesIO
class TestScript(unittest.TestCase):
def setUp(self):
self._old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
def _output(self):
self._stdout.seek(0)
return self._stdout.read()
def test_foo(self):
hello_test = HelloTest()
hello_test.foo("blub")
self.assertEqual(self._output(), "BLUB")
def tearDown(self):
sys.stdout = self._old_stdout
self._stdout.close()
You can also do that for stdin (and write to stdin to mock some input) and you can subclass TestIOWrapper if you need anything special to be done, like allowing non-unicode text to be sent to sys.stdoutwithout using sys.stdout.buffer(Python 2 vs. Python 3).
There is an example for that in this SO answer.
When you (still) use Python 2 only, then using StringIOmight be better than using the io module.
您也可以为 stdin 执行此操作(并写入 stdin 以模拟某些输入),并且如果您需要完成任何特殊操作,您可以将 TestIOWrapper 子类化,例如允许在sys.stdout不使用的情况下发送非 unicode 文本sys.stdout.buffer(Python 2 vs. Python 3 )。在这个 SO answer 中有一个例子。当您(仍然)仅使用 Python 2 时,使用StringIO可能比使用 io 模块更好。

