14.1 测试stdout输出¶

问题¶

你的程�中有个方法会输出到标准输出中(sys.stdout)。也就是说它会将文本打�到�幕上�。 你想写个测试��明它,给定一个输入,相应的输出能正常显示出�。

解决方案¶

使用 unittest.mock 模�中的 patch() 函数, 使用起��常简�,�以为�个测试模拟 sys.stdout 然�回滚, 并且�产生大�的临时��或在测试用例直接暴露状���。

作为一个例�,我们在 mymodule 模�中定义如下一个函数:

# mymodule.py

def urlprint(protocol, host, domain):
    url = '{}://{}.{}'.format(protocol, host, domain)
    print(url)

默认情况下内置的 print 函数会将输出��到 sys.stdout 。 为了测试输出真的在那里,你�以使用一个替身对象�模拟它,然�使用断言�确认结果。 使用 unittest.mock 模�的 patch() 方法�以很方便的在测试�行的上下文中替�对象, 并且当测试完�时候自动返回它们的原有状�。下�是对 mymodule 模�的测试代�:

from io import StringIO
from unittest import TestCase
from unittest.mock import patch
import mymodule

class TestURLPrint(TestCase):
    def test_url_gets_to_stdout(self):
        protocol = 'http'
        host = 'www'
        domain = 'example.com'
        expected_url = '{}://{}.{}\n'.format(protocol, host, domain)

        with patch('sys.stdout', new=StringIO()) as fake_out:
            mymodule.urlprint(protocol, host, domain)
            self.assertEqual(fake_out.getvalue(), expected_url)

讨论¶

urlprint() 函数接�三个�数,测试方法开始会先设置�一个�数的值。 expected_url ��被设置�包�期望的输出的字符串。

unittest.mock.patch() 函数被用作一个上下文管�器,使用 StringIO 对象�代替 sys.stdout . fake_out ��是在该进程中被创建的模拟对象。 在with语�中使用它�以执行��检查。当with语�结�时,patch 会将所有东西��到测试开始�的状�。 有一点需�注�的是�些对Python的C扩展�能会忽略掉 sys.stdout 的�置而直接写入到标准输出中。 �于篇幅,本节�会涉�到这方�的讲解,它适用于纯Python代�。 如果你真的需�在C扩展中�获I/O,你�以先打开一个临时文件,然�将标准输出�定�到该文件中。 更多关于�获以字符串形��获I/O和 StringIO 对象请�阅5.6�节。