2016-11-30 3 views
2

От this great answer Я научился анализировать парсы в своей собственной функции, чтобы упростить модульное тестирование.Как я могу проверить, бросает ли мой код соответствующие исключения argparse?

От this answer Я узнал, что иногда вам нужно выбросить собственные ошибки парсера, чтобы получить argparse для выполнения вашего поведения. Например .:

if not (args.process or args.upload): 
    parser.error('No action requested, add -process or -upload') 

Но это трудно проверить, если это то, что он должен, так как метания ошибка синтаксического анализа и выходит из программы. Так что-то вроде этого TestCase не будет работать:

def test_no_action_error(self): 
    '''Test if no action produces correct error''' 
    with self.assertRaises(ArgumentError) as cm: 
     args = parse_args(' ') 
    self.assertEqual('No action requested, add -process or -upload', str(cm.exception)) 

Комментарии от первого вопроса предполагают this question. Но я не понимаю, как использовать этот код в файле тестирования.

+0

Я не уверен, что понял вопрос. Что не так с ловушкой 'SystemExit'? – wim

+0

@wim ничего плохого в этом, я просто не мог найти здесь вопрос о SO, который сказал, что это (и как) – raphael

ответ

2

После небольшого взлома я нашел что-то, что пройдет тестирование. Предложения по удалению приветствия.

В моей основной программе я определил parse_args с некоторыми дополнительными ключевыми аргументами, которые будут использоваться только для тестирования.

def parse_args(args, prog = None, usage = None): 
    PARSER = argparse.ArgumentParser(prog=prog, usage=usage) 
    .... 

Тогда в классе тестирования для тестирования анализатор, добавив эти параметры, чтобы подавить использование и справочную информацию об ошибке, насколько это возможно.

class ArgParseTestCase(unittest.TestCase): 
    def __init__(self, *args, **kwargs): 
     self.testing_params = {'prog':'TESTING', 'usage':''} 
     super(ArgParseTestCase, self).__init__(*args, **kwargs) 

В файле тестирования определил этот контекст менеджер из this answer:

from contextlib import contextmanager 
from io import StringIO 

@contextmanager 
def capture_sys_output(): 
    capture_out, capture_err = StringIO(), StringIO() 
    current_out, current_err = sys.stdout, sys.stderr 
    try: 
     sys.stdout, sys.stderr = capture_out, capture_err 
     yield capture_out, capture_err 
    finally: 
     sys.stdout, sys.stderr = current_out, current_err 

А затем модифицировали тест на мой вопрос выше, чтобы быть что-то вроде:

def test_no_action_error(self): 
    '''Test if no action produces correct error''' 
    with self.assertRaises(SystemExit) as cm, capture_sys_output() as (stdout, stderr): 
     args = parse_args([' '], **self.testing_params) 
    self.assertEqual(2, cm.exception.code) 
    self.assertEqual('usage: \n TESTING: error: No action requested, add -process or -upload', 
        stderr.getvalue()) 

Теперь дополнительный текст в начале assertEqual не очень ... но тест проходит так, что я счастлив.

0

Вы поймаете неправильное исключение, просто.

Использование parser.errorwill trigger SystemExit, так что поймите, что вместо этого.

Чтобы сделать утверждение по этому сообщению, вам необходимо будет перенаправить sys.stderr. Эта информация не упоминается в объекте исключения в любом месте, она просто печатается перед выходом.

+1

в том, что 'with self.assertRaises (SystemExit):'? – raphael

0

test/test_argparse.py делает некоторые из такого рода испытаний:

Например:

class TestArgumentTypeError(TestCase): 

    def test_argument_type_error(self): 

     def spam(string): 
      raise argparse.ArgumentTypeError('spam!') 

     parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) 
     parser.add_argument('x', type=spam) 
     with self.assertRaises(ArgumentParserError) as cm: 
      parser.parse_args(['XXX']) 
     self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n', 
         cm.exception.stderr) 

Но ключ к этому в ErrorRaisingArgumentParser подкласса, определенного вблизи начала файла.

class ErrorRaisingArgumentParser(argparse.ArgumentParser): 

    def parse_args(self, *args, **kwargs): 
     parse_args = super(ErrorRaisingArgumentParser, self).parse_args 
     return stderr_to_parser_error(parse_args, *args, **kwargs) 

    def exit(self, *args, **kwargs): 
     exit = super(ErrorRaisingArgumentParser, self).exit 
     return stderr_to_parser_error(exit, *args, **kwargs) 

    def error(self, *args, **kwargs): 
     error = super(ErrorRaisingArgumentParser, self).error 
     return stderr_to_parser_error(error, *args, **kwargs) 

См. Этот файл для деталей. С перенаправлением stderr это становится немного сложнее. Возможно, больше, чем нужно.

+0

Думаю, я вижу, куда это может пойти. Но также кажется, что он включает в себя, по существу, дублирование определения парсера между кодом и тестовым кодом (первый блок кода тестирования добавляет аргументы в синтаксический анализатор). – raphael

+0

Я не выбрал пример, чтобы точно соответствовать вашему делу; он тестирует другой тип ошибок. Для вашего случая есть 2 проблемы - ловить или перенаправить 'sys.exit', и поймать сообщение stderr, которое идет с ним. – hpaulj

+0

Я нашел решение, которое работает для меня, см. Здесь [http://stackoverflow.com/a/40916320/4047679) – raphael