我有一些代码,例如:
class HelloTest:
def foo(self, msg):
self.bar(msg.upper())
def bar(self, msg):
print(msg)
使用标准库
unittest
,我们可以使用assertEqual
、assertTrue
等方法来验证从函数返回的内容。由于
foo
没有明确返回值,我该如何测试它是否执行了正确的操作?我有一些代码,例如:
class HelloTest:
def foo(self, msg):
self.bar(msg.upper())
def bar(self, msg):
print(msg)
unittest
,我们可以使用assertEqual
、assertTrue
等方法来验证从函数返回的内容。foo
没有明确返回值,我该如何测试它是否执行了正确的操作?正如另一个答案所提到的那样,您可以使用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更改。
我不太明白为什么每个人都想要检查foo
是否调用了bar
。
foo
有一些功能,这些功能需要进行测试。如果foo
使用bar
来实现其功能,那就不应该是我们关心的事情。我们应该测试foo(msg)
是否将msg.upper()
写入sys.stdout
。
你可以将sys.stdout
重定向到一个字符串缓冲区,然后检查该缓冲区的内容是否与你期望的相符。
示例:
import io
import sys
import unittest
class TestScript(unittest.TestCase):
def setUp(self):
self._old_stdout = sys.stdout
sys.stdout = io.TextIOWrapper(io.BytesIO(), sys.stdout.encoding)
def test_foo(self):
hello_test = HelloTest()
hello_test.foo('hello')
sys.stdout.seek(0)
self.assertEqual(sys.stdout.read(), 'HELLO\n')
def tearDown(self):
sys.stdout.close()
sys.stdout = self._old_stdout
if __name__ == '__main__':
unittest.main()
对于一个版本,其中 sys.stdout.write()
同时接受 Unicode 和字节字符串,请参见 this answer。
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()
以下是您的代码,可以完成与上述相同的任务:
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)
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
因此,你可以添加一个可选参数:
def bar(self, MSG, file=sys.stdout):
print(MSG, file=file)
通常情况下,它会输出到标准输出(stdout),但你可以在单元测试中传递自己的文件。
在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`
def foo(self, msg)
,因为并不是所有函数都会与标准输出交互。 - Yarkee
self.assertEqual(ob.msg, "SOME STRING")
这一行中,ob 是从哪里来的? - Isaac Sekamatteself.assertEqual(mock_bar.call_args[0][0], "一些字符串")
我会更新答案。 - aychedee