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

查看:125
本文介绍了如何测试依赖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.

这非常好用,因为调用了正确的子类,并且使用其他错误参数的任何用户都可以查看给定子类的正确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)

如果myargNone,则使用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.

您如何为python模块的argparse部分编写测试?

用于argparse的python单元测试

Argparse单元测试:禁止显示帮助信息

带有命令行参数的单元测试

使用unittest测试argparse-退出错误

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

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