How can I test whether my code is throwing the appropriate argparse exceptions?

孤者浪人 提交于 2019-12-12 03:55:18

问题


From this great answer I learned to put argument parsing into its own function to simplify unit testing.

From this answer I learned that sometimes you need to throw your own parser errors to get argparse to perform the behaviour you want. E.g.:

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

But it is hard to test if this does what it should since throwing the parser error also exits the program. So something like this TestCase won't work:

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))

The comments from the first question suggest this question. But I don't follow how to use this code within a testing file.


回答1:


After a bit of hacking away I've found something that will pass testing. Suggestions to remove cruft welcome.

In my main program I defined parse_args with some extra keyword args to be used for testing only.

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

Then in the testing class for testing the parser, adding these parameters to suppress usage and help information on an error as much as possible.

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

In the testing file defined this context manager from 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

And then modified the test in my question above to be something like:

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())

Now the extra text at the start of the assertEqual isn't pretty... but the test passes so I'm happy.




回答2:


test/test_argparse.py does some of this kind of testing:

For example:

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)

But the key to this the ErrorRaisingArgumentParser subclass defined near the start of the file.

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)

See that file for details. With stderr redirection it gets a bit complicated. Maybe more than really needed.



来源:https://stackoverflow.com/questions/40898755/how-can-i-test-whether-my-code-is-throwing-the-appropriate-argparse-exceptions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!