How can I check if my code has selected the appropriate argparse exceptions?

From this excellent answer, I learned how to parse parses in my own function in order to simplify unit testing.

From this answer I learned that sometimes you need to throw your own parser errors in order to get argparse to execute your behavior. For example:.

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

But it’s hard to check if this does what you need, because throwing a parser error also exits the program. So something like this TestCase will not 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 on the first question suggest this question . But I don’t understand how to use this code in the test file.

+2
python unit-testing argparse
Nov 30 '16 at 21:41
source share
3 answers

After a little hack, I found something that will pass the test. Suggestions for removing a welcoming greeting.

In my main program, I defined parse_args with some additional key arguments that will only be used for testing.

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

Then, in the test class to test the parser, add these parameters to suppress usage information and error help information 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 test file, this context manager defined 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 beginning of assertEqual not very ... but the test passes, so I'm happy.

+2
Dec 01 '16 at 17:01
source share
β€” -

You will catch the wrong exception, simply.

Using parser.error raises SystemExit , so catch this.

To make a statement in the message, you will have to redirect sys.stderr . This information is not mentioned in the exception object anywhere, it is simply printed before exiting.

0
Nov 30 '16 at 21:49
source share

test/test_argparse.py performs some of the following tests:

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 subclass of ErrorRaisingArgumentParser defined near the beginning 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 this file for more details. With stderr redirection, this gets a little trickier. Perhaps more than necessary.

0
Dec 01 '16 at 1:54 on
source share



All Articles