哇,我来晚了,但我最近遇到了这个问题,并且我认为我想出了一个更好的解决方案,所以在这里分享一下...
我正在开发一个包含十几个脚本的模块,所有脚本都以相同的复制粘贴方式结尾:
if __name__ == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
print(__doc__)
else:
sys.exit(main())
不算糟糕,但也不能测试。我的解决方案是在我的模块中编写一个新函数:
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())
然后在每个脚本文件的末尾放置这个gem:
run_script(__name__, __doc__, main)
从技术上讲,无论你的脚本是作为模块导入还是作为脚本运行,这个函数都会被无条件地运行。不过这没关系,因为除非脚本作为脚本运行,否则该函数实际上并不执行任何操作。所以代码覆盖率检查器会看到该函数已经运行,并说“是的,代码覆盖率达到了100%!”同时,我编写了三个测试来覆盖该函数本身:
@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)
太棒了!现在你可以编写可测试的main()
函数,将其作为脚本调用,100%测试覆盖率,并且不需要忽略你覆盖报告中的任何代码。
nose-cov
在底层使用了 coverage.py,因此具有上述内容的.coveragerc
文件将完美地工作。 - Joscha# pragma: no cover
,就像这样if __name__ == '__main__': # pragma: no cover
。个人认为这种方法会让代码变得混乱且难看,所以我认为mouad的答案是最好的解决方案,但其他人可能会发现它有用。 - Taylor D. Edmiston['"]
而不是.
,像这样:__name__ == ['"]__main__['"]:
。 - Taylor D. Edmiston