I have a function inside a module that creates an argparse:
def get_options(prog_version='1.0', prog_usage='', misc_opts=None):
options = [] if misc_opts is None else misc_opts
parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser()
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version))
parser.add_argument('-c', '--config', dest='config', required=True, help='the path to the configuration file')
for option in options:
if 'option' in option and 'destination' in option:
parser.add_argument(option['option'],
dest=option.get('destination', ''),
default=option.get('default', ''),
help=option.get('description', ''),
action=option.get('action', 'store'))
return parser.parse_args()
A sample myapp.py would be:
my_options = [
{
"option": "-s",
"destination": "remote_host",
"default": "127.0.0.1",
"description": "The remote server name or IP address",
"action": "store"
},
]
# Get Command Line Options
options = get_options(misc_opts=my_options)
print options.config
print options.remote_host
and this will be called as:
$> python myapp.py -c config.yaml
$> config.yaml
127.0.0.1
Now, I am trying to create a unit test for this function but my problem is that I can't pass command line parameters via test code.
# mytest.py
import unittest
from mymodule import get_options
class argParseTestCase(unittest.TestCase):
def test_parser(self):
options = get_options()
# ...pass the command line arguments...
self.assertEquals('config.yaml', options.config) # ofcourse this fails because I don't know how I will pass the command line arguments
My problem is that I need to pass the command line arguments to get_options() but I don't know how to do it properly.
Expected proper call: python mytest.py (-c config.yaml should be passed inside the test code somehow.)
What is "working"/not working right now:
python mytest.py -c config.yaml is also not working. Returns AttributeError: 'module' object has no attribute 'config' since it expects me to call argParseTestCase instead. In other words, python mytest.py -c argParseTestCase "works" but would ofcourse be an return AssertionError: 'config.yaml' != 'argParseTestCase'python mytest.py -v to run the unit test in verbose mode also fails. It returns:
test_parser (main.argParseTestCase) ... mytest.py 1.0 ERROR ERROR: test_parser (main.argParseTestCase)
Traceback (most recent call last): File "tests/unit_tests/mytest.py", line 376, in test_parser options = get_options() File "/root/test/lib/python2.7/site-packages/mymodule.py", line 61, in get_options return parser.parse_args()
File "/usr/local/lib/python2.7/argparse.py", line 1701, in parse_args args, argv = self.parse_known_args(args, namespace)
File "/usr/local/lib/python2.7/argparse.py", line 1733, in parse_known_args namespace, args = self._parse_known_args(args, namespace)
File "/usr/local/lib/python2.7/argparse.py", line 1939, in _parse_known_args start_index = consume_optional(start_index)
File "/usr/local/lib/python2.7/argparse.py", line 1879, in consume_optional take_action(action, args, option_string)
File "/usr/local/lib/python2.7/argparse.py", line 1807, in take_action action(self, namespace, argument_values, option_string)
File "/usr/local/lib/python2.7/argparse.py", line 1022, in call parser.exit(message=formatter.format_help())
File "/usr/local/lib/python2.7/argparse.py", line 2362, in exit _sys.exit(status) SystemExit: 0
I prefer explicitly passing arguments instead of relying on globally available attributes such as sys.argv (which parser.parse_args() does internally). Thus I usually use argparse by passing the list of arguments myself (to main() and subsequently get_options() and wherever you need them):
def get_options(args, prog_version='1.0', prog_usage='', misc_opts=None):
# ...
return parser.parse_args(args)
and then pass in the arguments
def main(args):
get_options(args)
if __name__ == "__main__":
main(sys.argv[1:])
that way I can replace and test any list of arguments I like
options = get_options(['-c','config.yaml'])
self.assertEquals('config.yaml', options.config)
Your error message stack is hard to read because it is in quoted form rather than code. But I think the -v argument is producing a sys.exit. version is like help - it's supposed to display a message and then exit. The -v is used by unittest, but is also read by your parser.
There is an argparse unittest module, test/test_argparse.py. You may need a development Python installation to see that. Some tests are straightforward, others use specialized testing structure. Some of that special code creates arguments in the same way you do with options.
The are two special issues:
generating the input. parse_args uses sys.argv[1:] unless its argv parameter is not None. So you can test a parser by either modifying the sys.argv list (unittest has already used your commandline values), or by passing a argv=None keyword argument into your function and on to parse_args. Trying to make a commandline meant for the unittest code to work with get_options is too complicated.
trapping the output, especially the sys.exit generated by errors. One option is to subclass ArgumentParser and give it a different error and/or exit method. Another is to wrap the function call in a try block.
unittest takes -c argument, but with a different syntax and meaning
-c, --catch Catch control-C and display results
and -v is verbose, not version.
=============
This tests the config argument (in a self contained one file form)
import unittest
import sys
#from mymodule import get_options
def get_options(argv=None, prog_version='1.0', prog_usage='', misc_opts=None):
# argv is optional test list; uses sys.argv[1:] is not provided
from argparse import ArgumentParser
options = [] if misc_opts is None else misc_opts
parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser()
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version))
parser.add_argument('-c', '--config', dest='config', help='the path to the configuration file')
for option in options:
if 'option' in option and 'destination' in option:
parser.add_argument(option['option'],
dest=option.get('destination', ''),
default=option.get('default', ''),
help=option.get('description', ''),
action=option.get('action', 'store'))
args = parser.parse_args(argv)
print('args',args)
return args
class argParseTestCase(unittest.TestCase):
def test_config(self):
sys.argv[1:]=['-c','config.yaml']
options = get_options()
self.assertEquals('config.yaml', options.config)
def test_version(self):
sys.argv[1:]=['-v']
with self.assertRaises(SystemExit):
get_options()
# testing version message requires redirecting stdout
# similarly for a misc_opts test
if __name__=='__main__':
unittest.main()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With