How to Write Tests for the Argparse Portion of a Python Module

How do you write tests for the argparse portion of a python module?

You should refactor your code and move the parsing to a function:

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

Then in your main function you should just call it with:

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

(where the first element of sys.argv that represents the script name is removed to not send it as an additional switch during CLI operation.)

In your tests, you can then call the parser function with whatever list of arguments you want to test it with:

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

This way you'll never have to execute the code of your application just to test the parser.

If you need to change and/or add options to your parser later in your application, then create a factory method:

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

You can later manipulate it if you want, and a test could look like:

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

How to test Python classes that depend on argparse?

Unittesting for argparse is tricky. There is a test/test_argparse.py file that is run as part of the overall Python unittest. But it has a complicated custom testing harness to handle most cases.

There are three basic issues, 1) calling parse_args with test values, 2) testing the resulting args, 3) testing for errors.

Testing the resulting args is relatively easy. And the argparse.Namespace class has simple __eq__ method so you can test one namespace against another.

There are two ways of testing inputs. One is to modify the sys.argv. Initially sys.argv has strings meant for the tester.

self.args = parser.parse_args()

tests sys.argv[1:] as a default. So if you change sys.argv you can test custom values.

But you can also give parse_args a custom list. The argparse docs uses this in most of its examples.

self.args = parser.parse_args(argv=myargv)

If myarg is None it uses sys.argv[1:]. Otherwise it uses that custom list.

Testing errors requires either a custom parse.error method (see docs) or wrapping the parse_args in a try/except block that can catch a sys.exit exception.

How do you write tests for the argparse portion of a python module?

python unittest for argparse

Argparse unit tests: Suppress the help message

Unittest with command-line arguments

Using unittest to test argparse - exit errors

Python unittest with argparse

use argv inside class TestThingy

def test_parser(self):
argv1 = ['--arg1', 'asdf', '--arg2', 'qwer']
parser = Thingy('name').parse_args(argv1)
self.assertEquals(parser.arg1,'asdf')

argv2 = ['--trigger_exception`, 'asdf`]
with self.assertRaise(Exception):
parser = Thingy('name').parse_args(argv2)

Python 2.6 Unittest assistance with parameters and argparse, how to solve?

To facilitate test class, I have modified the code as following:

  • Removed global variables (e.g.: HOSTNAME, SOMESTRING)
  • Passed parameters to functions
  • Returned string from function rather than printing (from message_test and main)

Updated code receiver.py:

#!python
import argparse
import sys

def argparse_msg():
return "testscript_example -H somehost -S somestring"

def check_arg(args=None):
parser = argparse.ArgumentParser(description="A Test Example", usage=argparse_msg())
parser.add_argument("-H", "--host",
help="HostName",
required=True)
parser.add_argument("-S", "--somestring",
help="HostName",
required=True)
results = parser.parse_args(args)
return (results.host, results.somestring)

def message_test(HOSTNAME, SOMESTRING):
return "{} {}".format(HOSTNAME, SOMESTRING)

def main(HOSTNAME, SOMESTRING):
return message_test(HOSTNAME, SOMESTRING)

if __name__ == "__main__":
HOSTNAME, SOMESTRING = check_arg(sys.argv[1:])
print(main(HOSTNAME, SOMESTRING))

Output of running receiver.py:

python receiver.py -H localhost -S demo
localhost demo

Test file (test_receiver.py):

from receiver import check_arg
import unittest

class ParserTest(unittest.TestCase):

def test_main(self):
HOSTNAME, SOMESTRING = check_arg(['-H', 'test', '-S', 'sample string'])
self.assertEqual(HOSTNAME, 'test')
self.assertEqual(SOMESTRING, 'sample string')

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

Output of running the test_receiver.py:

python test_receiver.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Testing arguments parsing with unittest and argparse

In the assertTrue statements you are passing a non-empty string which always evaluates as True.

If paramParser.main returns its arguments, you should do something like this:

def testOneIntValue(self):
result = paramParser.main(["-d C:\\Path\\to\\yourfile\\xxx.m", "-p 'xxxParamName'", "-v 3 "])
self.assertEqual(result.xxxParamName, 3) # access xxxParamName here the way you pass it

Also, you might want to read about the unittest module and check out this question - How do you write tests for argparse?.



Related Topics



Leave a reply



Submit