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
andmain
)
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
How to Get 'Real-Time' Information Back from a Subprocess.Popen in Python (2.5)
Why Doesn't List Have Safe "Get" Method Like Dictionary
Datetime Timezone Conversion Using Pytz
Class Inheritance in Python 3.7 Dataclasses
Except-Clause Deletes Local Variable
How to Access the Real Value of a Cell Using the Openpyxl Module for Python
Splitting on Last Delimiter in Python String
Stratified Train/Test-Split in Scikit-Learn
Extract Email Sub-Strings from Large Document
Defining "Boolness" of a Class in Python
Datetime from String in Python, Best-Guessing String Format
Rreplace - How to Replace the Last Occurrence of an Expression in a String
Python 2.X - Write Binary Output to Stdout
How Could I Use Batch Normalization in Tensorflow
Python Datetime to String Without Microsecond Component