如何测试依赖于 argparse 的 Python 类? [英] How to test Python classes that depend on argparse?

查看:25
本文介绍了如何测试依赖于 argparse 的 Python 类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下粘贴包含来自三个单独 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)

如果 myargNone 它使用 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 部分编写测试?

argparse 的 python 单元测试

Argparse 单元测试:抑制帮助消息

使用命令行参数进行单元测试

使用 unittest 测试 argparse - 退出错误

这篇关于如何测试依赖于 argparse 的 Python 类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆