如何测试依赖argparse的Python类? [英] How to test Python classes that depend on argparse?
问题描述
以下粘贴包含来自三个单独的Python文件的相关代码段.第一个是从命令行调用的脚本,该脚本在给定某些参数的情况下实例化CIPuller.发生的情况是该脚本被调用,例如:
script.py ci
(其他args会被argparse吞噬).
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).
第二个是称为Puller
的子类的一部分.第三个是Puller
子类的一部分,该子类称为CIPuller
.
The second is part of a subclass called Puller
. The third is part of a subclass of Puller
called CIPuller
.
这非常好用,因为调用了正确的子类,并且使用其他错误参数的任何用户都可以查看给定子类的正确arg,以及超类的通用参数. (尽管让我离线了解到,也许我应该使用 argparse子命令.)
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.)
我一直在努力为这些类编写测试.当前,我需要一个ArgumentParser
来实例化这些类,但是在测试中我不是从命令行中实例化某些东西,因此我的ArgumentParser
是没有用的.
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.
我尝试在测试工具中创建一个ArgumentParser
以传递给测试代码中的CIPuller's
构造函数,但是如果在那里使用add_argument
,argparse可以理解的是,当它调用add_argument
时,它会抱怨出现两个重复的参数.在CIPuller
构造函数中.
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
)
推荐答案
argparse
的单元测试很棘手.有一个test/test_argparse.py
文件作为整个Python单元测试的一部分运行.但是它具有复杂的自定义测试工具,可以处理大多数情况.
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.
存在三个基本问题:1)使用测试值调用parse_args
,2)测试生成的args,3)测试错误.
There are three basic issues, 1) calling parse_args
with test values, 2) testing the resulting args, 3) testing for errors.
测试生成的args
相对容易.而且argparse.Namespace
类具有简单的__eq__
方法,因此您可以针对另一个命名空间进行测试.
Testing the resulting args
is relatively easy. And the argparse.Namespace
class has simple __eq__
method so you can test one namespace against another.
有两种测试输入的方法.一种是修改sys.argv
.最初,sys.argv
具有用于测试人员的字符串.
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()
默认测试sys.argv[1:]
.因此,如果您更改sys.argv
,则可以测试自定义值.
tests sys.argv[1:]
as a default. So if you change sys.argv
you can test custom values.
但是您也可以给parse_args
一个自定义列表. argparse
文档在其大多数示例中都使用了此功能.
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)
如果myarg
是None
,则使用sys.argv[1:]
.否则,它将使用该自定义列表.
If myarg
is None
it uses sys.argv[1:]
. Otherwise it uses that custom list.
测试错误需要自定义parse.error
方法(请参阅docs)或将parse_args
包装在可以捕获sys.exit
异常的try/except
块中.
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.
这篇关于如何测试依赖argparse的Python类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!