<p>喔,我来晚了点,但我最近碰到了这个问题,我想我想出了更好的解决办法,所以。。。</p>
<p>我正在开发一个模块,其中包含了十几个脚本,所有脚本都以这个完全相同的副本结尾:</p>
<pre><code>if __name__ == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
print(__doc__)
else:
sys.exit(main())
</code></pre>
<p>不可怕,当然,但也不可测试。我的解决方案是在我的一个模块中编写一个新函数:</p>
<pre><code>def run_script(name, doc, main):
"""Act like a script if we were invoked like a script."""
if name == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
sys.stdout.write(doc)
else:
sys.exit(main())
</code></pre>
<p>然后将这个gem放在每个脚本文件的末尾:</p>
<pre><code>run_script(__name__, __doc__, main)
</code></pre>
<p>从技术上讲,无论脚本是作为模块导入还是作为脚本运行,此函数都将无条件运行。但这是可以的,因为除非脚本作为脚本运行,否则函数实际上不会执行任何操作。所以代码覆盖率看到函数运行并说“是的,100%的代码覆盖率!”同时,我写了三个测试来覆盖函数本身:</p>
<pre><code>@patch('mymodule.utils.sys')
def test_run_script_as_import(self, sysMock):
"""The run_script() func is a NOP when name != __main__."""
mainMock = Mock()
sysMock.argv = []
run_script('some_module', 'docdocdoc', mainMock)
self.assertEqual(mainMock.mock_calls, [])
self.assertEqual(sysMock.exit.mock_calls, [])
self.assertEqual(sysMock.stdout.write.mock_calls, [])
@patch('mymodule.utils.sys')
def test_run_script_as_script(self, sysMock):
"""Invoke main() when run as a script."""
mainMock = Mock()
sysMock.argv = []
run_script('__main__', 'docdocdoc', mainMock)
mainMock.assert_called_once_with()
sysMock.exit.assert_called_once_with(mainMock())
self.assertEqual(sysMock.stdout.write.mock_calls, [])
@patch('mymodule.utils.sys')
def test_run_script_with_help(self, sysMock):
"""Print help when the user asks for help."""
mainMock = Mock()
for h in ('-h', '--help'):
sysMock.argv = [h]
run_script('__main__', h*5, mainMock)
self.assertEqual(mainMock.mock_calls, [])
self.assertEqual(sysMock.exit.mock_calls, [])
sysMock.stdout.write.assert_called_with(h*5)
</code></pre>
<p>责怪!现在您可以编写一个可测试的<code>main()</code>,将其作为脚本调用,具有100%的测试覆盖率,并且不需要忽略覆盖率报告中的任何代码。</p>