|
1 | 1 | ==============================
|
2 |
| -14.1 测试输出到标准输出上 |
| 2 | +14.1 测试stdout输出 |
3 | 3 | ==============================
|
4 | 4 |
|
5 | 5 | ----------
|
6 | 6 | 问题
|
7 | 7 | ----------
|
8 |
| -You have a program that has a method whose output goes to standard Output |
9 |
| -(sys.stdout). This almost always means that it emits text to the screen. You’d like to |
10 |
| -write a test for your code to prove that, given the proper input, the proper output is |
11 |
| -displayed. |
| 8 | +你的程序中有个方法会输出到标准输出中(sys.stdout)。也就是说它会将文本打印到屏幕上面。 |
| 9 | +你想写个测试来证明它,给定一个输入,相应的输出能正常显示出来。 |
12 | 10 |
|
13 | 11 | |
|
14 | 12 |
|
15 | 13 | ----------
|
16 | 14 | 解决方案
|
17 | 15 | ----------
|
18 |
| -Using the unittest.mock module’s patch() function, it’s pretty simple to mock out |
19 |
| -sys.stdout for just a single test, and put it back again, without messy temporary vari‐ |
20 |
| -ables or leaking mocked-out state between test cases. |
21 |
| -Consider, as an example, the following function in a module mymodule: |
22 |
| - |
23 |
| -# mymodule.py |
24 |
| - |
25 |
| -def urlprint(protocol, host, domain): |
26 |
| - url = '{}://{}.{}'.format(protocol, host, domain) |
27 |
| - print(url) |
28 |
| - |
29 |
| -The built-in print function, by default, sends output to sys.stdout. In order to test |
30 |
| -that output is actually getting there, you can mock it out using a stand-in object, and |
31 |
| -then make assertions about what happened. Using the unittest.mock module’s patch() |
32 |
| -method makes it convenient to replace objects only within the context of a running test, |
33 |
| -returning things to their original state immediately after the test is complete. Here’s the |
34 |
| -test code for mymodule: |
35 |
| - |
36 |
| -from io import StringIO |
37 |
| -from unittest import TestCase |
38 |
| -from unittest.mock import patch |
39 |
| -import mymodule |
40 |
| - |
41 |
| -class TestURLPrint(TestCase): |
42 |
| - def test_url_gets_to_stdout(self): |
43 |
| - protocol = 'http' |
44 |
| - host = 'www' |
45 |
| - domain = 'example.com' |
46 |
| - expected_url = '{}://{}.{}\n'.format(protocol, host, domain) |
47 |
| - |
48 |
| - with patch('sys.stdout', new=StringIO()) as fake_out: |
49 |
| - mymodule.urlprint(protocol, host, domain) |
50 |
| - self.assertEqual(fake_out.getvalue(), expected_url) |
| 16 | +使用 ``unittest.mock`` 模块中的 ``patch()`` 函数, |
| 17 | +使用起来非常简单,可以为单个测试模拟 ``sys.stdout`` 然后回滚, |
| 18 | +并且不产生大量的临时变量或在测试用例直接暴露状态变量。 |
| 19 | + |
| 20 | +作为一个例子,我们在 ``mymodule`` 模块中定义如下一个函数: |
| 21 | + |
| 22 | +.. code-block:: python |
| 23 | +
|
| 24 | + # mymodule.py |
| 25 | +
|
| 26 | + def urlprint(protocol, host, domain): |
| 27 | + url = '{}://{}.{}'.format(protocol, host, domain) |
| 28 | + print(url) |
| 29 | +
|
| 30 | +默认情况下内置的 ``print`` 函数会将输出发送到 ``sys.stdout`` 。 |
| 31 | +为了测试输出真的在那里,你可以使用一个替身对象来模拟它,然后使用断言来确认结果。 |
| 32 | +使用 ``unittest.mock`` 模块的 ``patch()`` 方法可以很方便的在测试运行的上下文中替换对象, |
| 33 | +并且当测试完成时候自动返回它们的原有状态。下面是对 ``mymodule`` 模块的测试代码: |
| 34 | + |
| 35 | +.. code-block:: python |
| 36 | +
|
| 37 | + from io import StringIO |
| 38 | + from unittest import TestCase |
| 39 | + from unittest.mock import patch |
| 40 | + import mymodule |
| 41 | +
|
| 42 | + class TestURLPrint(TestCase): |
| 43 | + def test_url_gets_to_stdout(self): |
| 44 | + protocol = 'http' |
| 45 | + host = 'www' |
| 46 | + domain = 'example.com' |
| 47 | + expected_url = '{}://{}.{}\n'.format(protocol, host, domain) |
| 48 | +
|
| 49 | + with patch('sys.stdout', new=StringIO()) as fake_out: |
| 50 | + mymodule.urlprint(protocol, host, domain) |
| 51 | + self.assertEqual(fake_out.getvalue(), expected_url) |
51 | 52 |
|
52 | 53 | |
|
53 | 54 |
|
54 | 55 | ----------
|
55 | 56 | 讨论
|
56 | 57 | ----------
|
57 |
| -The urlprint() function takes three arguments, and the test starts by setting up dummy |
58 |
| -arguments for each one. The expected_url variable is set to a string containing the |
59 |
| -expected output. |
60 |
| -To run the test, the unittest.mock.patch() function is used as a context manager to |
61 |
| -replace the value of sys.stdout with a StringIO object as a substitute. The fake_out |
62 |
| -variable is the mock object that’s created in this process. This can be used inside the |
63 |
| -body of the with statement to perform various checks. When the with statement com‐ |
64 |
| -pletes, patch conveniently puts everything back the way it was before the test ever ran. |
65 |
| -It’s worth noting that certain C extensions to Python may write directly to standard |
66 |
| -output, bypassing the setting of sys.stdout. This recipe won’t help with that scenario, |
67 |
| -but it should work fine with pure Python code (if you need to capture I/O from such C |
68 |
| -extensions, you can do it by opening a temporary file and performing various tricks |
69 |
| -involving file descriptors to have standard output temporarily redirected to that file). |
70 |
| -More information about capturing IO in a string and StringIO objects can be found in |
71 |
| -Recipe 5.6. |
| 58 | +``urlprint()`` 函数接受三个参数,测试方法开始会先设置每一个参数的值。 |
| 59 | +``expected_url`` 变量被设置成包含期望的输出的字符串。 |
| 60 | + |
| 61 | +``unittest.mock.patch()`` 函数被用作一个上下文管理器,使用 ``StringIO`` 对象来代替 ``sys.stdout`` . |
| 62 | +``fake_out`` 变量是在该进程中被创建的模拟对象。 |
| 63 | +在with语句中使用它可以执行各种检查。当with语句结束时,``patch`` 会将所有东西恢复到测试开始前的状态。 |
| 64 | +有一点需要注意的是某些对Python的C扩展可能会忽略掉 ``sys.stdout`` 的配置二直接写入到标准输出中。 |
| 65 | +限于篇幅,本节不会涉及到这方面的讲解,它适用于纯Python代码。 |
| 66 | +如果你真的需要在C扩展中捕获I/O,你可以先打开一个临时文件,然后将标准输出重定向到该文件中。 |
| 67 | +更多关于捕获以字符串形式捕获I/O和 ``StringIO`` 对象请参阅5.6小节。 |
72 | 68 |
|
0 commit comments