source

python 모듈의 argparse 부분에 대한 테스트는 어떻게 작성합니까?

lovecheck 2022. 11. 8. 21:12
반응형

python 모듈의 argparse 부분에 대한 테스트는 어떻게 작성합니까?

argparse 라이브러리를 사용하는 Python 모듈을 가지고 있습니다.코드 베이스의 해당 섹션에 대한 테스트는 어떻게 작성해야 합니까?

코드를 리팩터링하고 파싱을 함수로 이동해야 합니다.

def parse_args(args):
    parser = argparse.ArgumentParser(...)
    parser.add_argument...
    # ...Create your parser as you like...
    return parser.parse_args(args)

그럼 네 안에main호출해야 할 함수는 다음과 같습니다.

parser = parse_args(sys.argv[1:])

(여기서 첫 번째 요소는sys.argv스크립트명을 나타내는 것은, CLI 조작중에 추가 스위치로서 송신하지 않기 위해서 삭제됩니다).

그런 다음 테스트하는 인수 목록을 사용하여 파서 함수를 호출할 수 있습니다.

def test_parser(self):
    parser = parse_args(['-l', '-m'])
    self.assertTrue(parser.long)
    # ...and so on.

이렇게 하면 파서를 테스트하기 위해 애플리케이션 코드를 실행할 필요가 없습니다.

나중에 응용 프로그램에서 파서를 변경하거나 옵션을 추가해야 할 경우 공장 출하 시 메서드를 만듭니다.

def create_parser():
    parser = argparse.ArgumentParser(...)
    parser.add_argument...
    # ...Create your parser as you like...
    return parser

나중에 필요에 따라 조작할 수 있습니다.테스트는 다음과 같습니다.

class ParserTest(unittest.TestCase):
    def setUp(self):
        self.parser = create_parser()

    def test_something(self):
        parsed = self.parser.parse_args(['--something', 'test'])
        self.assertEqual(parsed.something, 'test')

"parse partes parts"는 약간 애매하기 때문에 이 답변은 한 부분에 초점을 맞추고 있습니다.parse_args방법.이는 명령줄과 상호 작용하여 전달된 모든 값을 가져오는 방법입니다.기본적으로, 당신은 무엇을 조롱할 수 있다.parse_args명령줄에서 실제로 값을 가져올 필요가 없도록 를 반환합니다.mock python 버전 2.6-3.2의 경우 pip을 통해 패키지를 설치할 수 있습니다.표준 라이브러리의 일부입니다.unittest.mock버전 3.3 이후입니다.

import argparse
try:
    from unittest import mock  # python 3.3+
except ImportError:
    import mock  # python 2.6-3.2


@mock.patch('argparse.ArgumentParser.parse_args',
            return_value=argparse.Namespace(kwarg1=value, kwarg2=value))
def test_command(mock_args):
    pass

명령어 메서드의 arg를 모두 포함해야 합니다.Namespace통과되지 못하더라도요이 args에 값을 부여합니다.None. (문서 참조)이 스타일은 메서드 인수마다 다른 값이 전달되는 경우 신속하게 테스트를 수행할 때 유용합니다.조롱하고 싶다면Namespace테스트에 대한 완전한 argparse 비의존성을 위해 실제와 동일하게 동작하는지 확인합니다.Namespace학급.

argparse 라이브러리의 첫 번째 스니펫을 사용하는 예를 다음에 나타냅니다.

# test_mock_argparse.py
import argparse
try:
    from unittest import mock  # python 3.3+
except ImportError:
    import mock  # python 2.6-3.2


def main():
    parser = argparse.ArgumentParser(description='Process some integers.')
    parser.add_argument('integers', metavar='N', type=int, nargs='+',
                        help='an integer for the accumulator')
    parser.add_argument('--sum', dest='accumulate', action='store_const',
                        const=sum, default=max,
                        help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args)  # NOTE: this is how you would check what the kwargs are if you're unsure
    return args.accumulate(args.integers)


@mock.patch('argparse.ArgumentParser.parse_args',
            return_value=argparse.Namespace(accumulate=sum, integers=[1,2,3]))
def test_command(mock_args):
    res = main()
    assert res == 6, "1 + 2 + 3 = 6"


if __name__ == "__main__":
    print(main())

를 만듭니다.main()함수 테이크argv디폴트에서는 읽도록 하는 것이 아니라 인수로 합니다.

# mymodule.py
import argparse
import sys


def main(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('-a')
    process(**vars(parser.parse_args(args)))
    return 0


def process(a=None):
    pass

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

그러면 정상적으로 테스트할 수 있습니다.

import mock

from mymodule import main


@mock.patch('mymodule.process')
def test_main(process):
    main([])
    process.assert_call_once_with(a=None)


@mock.patch('foo.process')
def test_main_a(process):
    main(['-a', '1'])
    process.assert_call_once_with(a='1')

저는 원래 서빙 스크립트를 수정하고 싶지 않아서 그냥 조롱했어요.sys.argv아르파르세로 나누다

from unittest.mock import patch

with patch('argparse._sys.argv', ['python', 'serve.py']):
    ...  # your test code here

argparse 구현이 변경되지만 빠른 테스트스크립트에 충분할 경우 이 문제는 해결됩니다.어쨌든 테스트 대본에서는 특정성보다 감성이 훨씬 더 중요하다.

  1. 하려면 arg를 합니다.sys.argv.append()에 전화해요.parse()이치노
  2. 플래그와 덤프 args 플래그가 있는 배치/배시 파일에서 호출합니다.
  3. 은 모두 .if __name__ == "__main__":콜 해석과 덤프/다운로드 결과를 배치/다운로드 파일에서 테스트합니다.

parse_argsSystemExit 두 가지를 수 .

import contextlib
import io
import sys

@contextlib.contextmanager
def captured_output():
    new_out, new_err = io.StringIO(), io.StringIO()
    old_out, old_err = sys.stdout, sys.stderr
    try:
        sys.stdout, sys.stderr = new_out, new_err
        yield sys.stdout, sys.stderr
    finally:
        sys.stdout, sys.stderr = old_out, old_err

def validate_args(args):
    with captured_output() as (out, err):
        try:
            parser.parse_args(args)
            return True
        except SystemExit as e:
            return False

합니다(stderr를 사용).err.seek(0); err.read()하지만 일반적으로 이러한 세분화는 필요하지 않습니다.

이제 또는 원하는 테스트를 사용할 수 있습니다.

assertTrue(validate_args(["-l", "-m"]))

, 다른 에러를 .SystemExit

def validate_args(args):
    with captured_output() as (out, err):
        try:
            return parser.parse_args(args)
        except SystemExit as e:
            err.seek(0)
            raise argparse.ArgumentError(err.read())

파서를 테스트하는 간단한 방법은 다음과 같습니다.

parser = ...
parser.add_argument('-a',type=int)
...
argv = '-a 1 foo'.split()  # or ['-a','1','foo']
args = parser.parse_args(argv)
assert(args.a == 1)
...

다른 은 수정하는 입니다.sys.argv및를 호출합니다.args = parser.parse_args()

시험에는 가 있습니다.argparselib/test/test_argparse.py

에서 할 때argparse.ArgumentParser.parse_args함수에 , 는 가끔 떤떤함, 는는 a a를 사용한다.namedtuple실험용 논거를 조롱하기 위해.

import unittest
from collections import namedtuple
from my_module import main

class TestMyModule(TestCase):

    args_tuple = namedtuple('args', 'arg1 arg2 arg3 arg4')

    def test_arg1(self):
        args = TestMyModule.args_tuple("age > 85", None, None, None)
        res = main(args)
        assert res == ["55289-0524", "00591-3496"], 'arg1 failed'

    def test_arg2(self):
        args = TestMyModule.args_tuple(None, [42, 69], None, None)
        res = main(args)
        assert res == [], 'arg2 failed'

if __name__ == '__main__':
    unittest.main()

명령어 출력이 아닌 CLI(커맨드 라인 인터페이스) 테스트용으로 다음과 같은 작업을 수행했습니다.

import pytest
from argparse import ArgumentParser, _StoreAction

ap = ArgumentParser(prog="cli")
ap.add_argument("cmd", choices=("spam", "ham"))
ap.add_argument("-a", "--arg", type=str, nargs="?", default=None, const=None)
...

def test_parser():
    assert isinstance(ap, ArgumentParser)
    assert isinstance(ap, list)
    args = {_.dest: _ for _ in ap._actions if isinstance(_, _StoreAction)}
    
    assert args.keys() == {"cmd", "arg"}
    assert args["cmd"] == ("spam", "ham")
    assert args["arg"].type == str
    assert args["arg"].nargs == "?"
    ...

언급URL : https://stackoverflow.com/questions/18160078/how-do-you-write-tests-for-the-argparse-portion-of-a-python-module

반응형