Ого, я немного опоздал на вечеринку, но недавно я столкнулся с этой проблемой, и я думаю, что нашел лучшее решение, так что вот оно ...
Я работал надмодуль, который содержал дюжину или около того скриптов, каждый из которых заканчивался этой копировальной пастой:
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())
и затем поместить этот драгоценный камень в конец каждого файла сценария:
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% тестовое покрытие, и вам не нужно игнорировать какой-либо код в вашем отчете о покрытии.