我是一名蟒蛇。在这些日子里,我正在驾驶自己对我项目中的一些核心模块进行更完整的单元测试。 因为我们总是用方法'assertEqual','assertTrue'等进行单元测试,所以这些方法都需要来自被测函数的返回值,我想知道如何在没有返回值的情况下对某些函数进行普通单元测试。
我想在这里展示一个小例子,如何在HelloTest中测试函数def foo(self,msg)?
class HelloTest(object):
def foo(self, msg):
MSG = msg.upper()
self.bar(MSG)
def bar(self, MSG):
print MSG
答案 0 :(得分:9)
如上所述,您可以使用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")
这会在HelloTest上修补bar
方法,并将其替换为记录针对它的调用的模拟对象。
嘲弄是一个兔子洞。只有在你必须这样做时才这样做,因为它确实使你的测试变得脆弱。例如,您永远不会注意到模拟对象的API更改。
答案 1 :(得分:8)
在这种特殊情况下,我会模拟打印,然后在我的断言中使用模拟。
在Python中,您将使用Mock package来模拟。
答案 2 :(得分:8)
我不太明白为什么每个人都想检查foo是否会调用吧。
Foo具有一些功能,需要测试此功能。如果foo使用bar来做这件事,那不应该是我关注的问题。
期望的结果是,在调用foo(msg)
之后,是msg.upper()
被发送到stdout。
您可以redirect stdout to a string buffer并检查此字符串缓冲区的内容是否符合您的预期。
示例:
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()
您也可以为stdin执行此操作(并写入stdin以模拟某些输入)并且如果您需要执行任何特殊操作,则可以继承TestIOWrapper,例如允许将非unicode文本发送到sys.stdout
使用sys.stdout.buffer
(Python 2与Python 3)。
在this SO answer中有一个例子。
当您(仍然)仅使用Python 2时,使用StringIO
可能比使用io模块更好。
答案 3 :(得分:3)
您的代码可以如下所示,其执行与上述相同的任务:
class HelloTest(object):
def foo(self, msg):
self.msg = msg.upper()
self.bar()
def bar(self):
print self.msg
单元测试是:
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)
答案 4 :(得分:2)
在Python 3中,您可以tell print
where to print to:
print(* objects,sep ='',end ='\ n',file = sys.stdout,flush = False)
所以添加一个可选参数:
def bar(self, MSG, file=sys.stdout):
print(MSG, file=file)
在正常使用中,它会打印到stdout,但对于单元测试,您可以传递自己的文件。
在Python 2中,它有点麻烦,但你可以redirect stdout to a StringIO buffer:
import StringIO
import sys
out = StringIO.StringIO()
sys.stdout = out
# run unit tests
sys.stdout = sys.__stdout__
# check the contents of `out`
答案 5 :(得分:2)
感谢@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()