"if __name__ == '__main__'"콘텐츠를 테스트하거나 모의하는 방법
다음과 같은 모듈이 있다고 가정합니다.
def main():
pass
if __name__ == "__main__":
main()
하반부에 대한 단위 테스트를 작성하고 싶습니다 (100 % 커버리지를 달성하고 싶습니다). import / -setting 메커니즘 을 수행 하는 runpy 내장 모듈을 발견 __name__
했지만, main () 함수가 호출 되었는지 모의하거나 확인하는 방법을 알아낼 수 없습니다 .
이것이 내가 지금까지 시도한 것입니다.
import runpy
import mock
@mock.patch('foobar.main')
def test_main(self, main):
runpy.run_module('foobar', run_name='__main__')
main.assert_called_once_with()
if __name__ == '__main__'
커버리지 보고서에서 를 제외하는 다른 대안을 선택하겠습니다. 물론 테스트에서 main () 함수에 대한 테스트 케이스가 이미있는 경우에만 그렇게 할 수 있습니다.
전체 스크립트에 대해 새 테스트 케이스를 작성하는 대신 제외하기로 선택한 이유는 내가 이미 언급했듯이 main()
함수에 대한 테스트 케이스가 이미있는 경우 스크립트에 대해 다른 테스트 케이스를 추가한다는 사실 때문입니다. 100 % 적용)은 복제 된 것입니다.
제외하는 방법 if __name__ == '__main__'
은 커버리지 구성 파일을 작성하고 섹션 보고서에 추가 할 수 있습니다.
[report]
exclude_lines =
if __name__ == .__main__.:
커버리지 구성 파일에 대한 자세한 정보는 여기 에서 찾을 수 있습니다 .
이것이 도움이되기를 바랍니다.
명령문이 imp
아닌 모듈을 사용하여이를 수행 할 수 있습니다 import
. 문의 문제 import
는 '__main__'
에 할당 할 기회를 얻기 전에 import 문의 일부로 실행 테스트가 실행된다는 것 runpy.__name__
입니다.
예를 들어 다음 imp.load_source()
과 같이 사용할 수 있습니다 .
import imp
runpy = imp.load_source('__main__', '/path/to/runpy.py')
첫 번째 매개 변수는 __name__
가져온 모듈에 할당됩니다 .
우와, 파티에 조금 늦었지만 최근에이 문제를 만났고 더 나은 해결책을 찾은 것 같아서 여기 있습니다.
저는이 정확한 카피 파스타로 끝나는 12 개 정도의 스크립트를 포함하는 모듈을 작업하고있었습니다.
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 % 테스트 범위를 가지며, 범위 보고서의 코드를 무시할 필요가 없습니다.
한 가지 접근 방식은 모듈을 스크립트 (예 : os.system (...))로 실행하고 stdout 및 stderr 출력을 예상 값과 비교하는 것입니다.
My solution is to use imp.load_source()
and force an exception to be raised early in main()
by not providing a required CLI argument, providing a malformed argument, setting paths in such a way that a required file is not found, etc.
import imp
import os
import sys
def mainCond(testObj, srcFilePath, expectedExcType=SystemExit, cliArgsStr=''):
sys.argv = [os.path.basename(srcFilePath)] + (
[] if len(cliArgsStr) == 0 else cliArgsStr.split(' '))
testObj.assertRaises(expectedExcType, imp.load_source, '__main__', srcFilePath)
Then in your test class you can use this function like this:
def testMain(self):
mainCond(self, 'path/to/main.py', cliArgsStr='-d FailingArg')
참고URL : https://stackoverflow.com/questions/5850268/how-to-test-or-mock-if-name-main-contents
'Development Tip' 카테고리의 다른 글
JDBC를 사용하여 .sql 스크립트 파일을 실행하는 방법 (0) | 2020.11.28 |
---|---|
주어진 문자열에 대해 모든 고유 한 하위 문자열 생성 (0) | 2020.11.28 |
데이터베이스에 구애받지 않는 Play 애플리케이션을 작성하고 최초 데이터베이스 초기화를 수행하는 방법은 무엇입니까? (0) | 2020.11.28 |
왜`std :: initializer_list`는 종종 값으로 전달됩니까? (0) | 2020.11.28 |
기본 Move 생성자가 noexcept로 정의되어 있습니까? (0) | 2020.11.28 |