如何测试依赖于 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
.
这非常有效,因为调用了正确的子类,任何使用错误的其他参数的用户都可以看到给定子类的正确参数,以及来自超类的通用参数.(虽然我在离线时意识到也许我应该使用 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 在 CIPuller
构造函数中调用 add_argument
时会抱怨双(重复)参数,这是可以理解的.
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) 测试结果参数,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
方法(请参阅文档)或将 parse_args
包装在 try/except
块中,该块可以捕获 sys.exit
异常.
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.
你好吗为 python 模块的 argparse 部分编写测试?
使用 unittest 测试 argparse - 退出错误
这篇关于如何测试依赖于 argparse 的 Python 类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!