The below paste contains relevant snippets from three separate Python files. The first is a script called from the command line which instantiates CIPuller given certain arguments. What happens is that the script gets called with something like:
script.py ci
(other args to be swallowed by argparse).
The second is part of a subclass called Puller
. The third is part of a subclass of Puller
called CIPuller
.
This works wonderfully, as the correct subclass is called, and any user using the wrong other args gets to see the correct args for their given subclass, plus the generic arguments from the superclass. (Although I was made aware offline that perhaps I should use argparse sub-commands for this.)
I'm stuck trying to write tests for these classes. Currently, I need an ArgumentParser
to instantiate the classes, but in testing I'm not instantiating things from the command line, hence my ArgumentParser
is useless.
I tried creating an ArgumentParser
in the test harness to pass to CIPuller's
constructor in the test code, but if I use add_argument
there, argparse understandably complains about double (duplicate) arguments when it calls add_argument
in the CIPuller
constructor.
What would be a suitable design to test these classes with arguments?
#!/usr/bin/env python
from ci_puller import CIPuller
import argparse
import sys
# Using sys.argv[1] for the argument here, as we don't want to pass that onto
# the subclasses, which should receive a vanilla ArgumentParser
puller_type = sys.argv.pop(1)
parser = argparse.ArgumentParser(
description='Throw data into Elasticsearch.'
)
if puller_type == 'ci':
puller = CIPuller(parser, 'single')
else:
raise ValueError("First parameter must be a supported puller. Exiting.")
puller.run()
class Puller(object):
def __init__(self, parser, insert_type):
self.add_arguments(parser)
self.args = parser.parse_args()
self.insert_type = insert_type
def add_arguments(self,parser):
parser.add_argument(
"-d", "--debug",
help="print debug info to stdout",
action="store_true"
)
parser.add_argument(
"--dontsend",
help="don't actually send anything to Elasticsearch",
action="store_true"
)
parser.add_argument(
"--host",
help="override the default host that the data is sent to",
action='store',
default='kibana.munged.tld'
)
class CIPuller(Puller):
def __init__(self, parser, insert_type):
self.add_arguments(parser)
self.index_prefix = "code"
self.doc_type = "cirun"
self.build_url = ""
self.json_url = ""
self.result = []
super(CIPuller, self).__init__(parser, insert_type)
def add_arguments(self, parser):
parser.add_argument(
'--buildnumber',
help='CI build number',
action='store',
required=True
)
parser.add_argument(
'--testtype',
help='Job type per CI e.g. minitest / feature',
choices=['minitest', 'feature'],
required=True
)
parser.add_argument(
'--app',
help='App e.g. sapi / stats',
choices=['sapi', 'stats'],
required=True
)
split() # or ['-a','1','foo'] args = parser. parse_args(argv) assert(args. a == 1) ... This should be the accepted answer: The simplest way to test different values given to each argument.
Run all Python tests in a directoryFrom the context menu, select the corresponding run command. If the directory contains tests that belong to the different testing frameworks, select the configuration to be used. For example, select Run 'All tests in: <directory name>' Run pytest in <directory name>'.
parse_args will take the arguments you provide on the command line when you run your program and interpret them according to the arguments you have added to your ArgumentParser object.
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
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