Whoa, ich bin ein bisschen zu spät zur Party, aber ich bin kürzlich auf dieses Problem gestoßen und ich denke, ich habe eine bessere Lösung gefunden, also hier ist es ...
Ich habe an einem Modul gearbeitet, das ungefähr ein Dutzend Skripte enthielt, die alle mit genau dieser Copypasta endeten:
if __name__ == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
print(__doc__)
else:
sys.exit(main())
Sicher nicht schrecklich, aber auch nicht testbar. Meine Lösung bestand darin, eine neue Funktion in eines meiner Module zu schreiben:
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())
und platzieren Sie dieses Juwel am Ende jeder Skriptdatei:
run_script(__name__, __doc__, main)
Technisch gesehen wird diese Funktion bedingungslos ausgeführt, unabhängig davon, ob Ihr Skript als Modul importiert oder als Skript ausgeführt wurde. Dies ist jedoch in Ordnung, da die Funktion nur dann etwas tut , wenn das Skript als Skript ausgeführt wird. Die Codeabdeckung sieht also, dass die Funktion ausgeführt wird und sagt "Ja, 100% Codeabdeckung!" In der Zwischenzeit habe ich drei Tests geschrieben, um die Funktion selbst abzudecken:
@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)
Schuld! Jetzt können main()
Sie ein Testobjekt schreiben , es als Skript aufrufen, eine 100% ige Testabdeckung haben und müssen keinen Code in Ihrem Abdeckungsbericht ignorieren.