2016-06-08 52 views
1

蟒單元測試我有一個模塊內的功能,創建一個​​:爲argparse

def get_options(prog_version='1.0', prog_usage='', misc_opts=None): 
    options = [] if misc_opts is None else misc_opts 
    parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser() 
    parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version)) 
    parser.add_argument('-c', '--config', dest='config', required=True, help='the path to the configuration file') 

    for option in options: 
     if 'option' in option and 'destination' in option: 
      parser.add_argument(option['option'], 
           dest=option.get('destination', ''), 
           default=option.get('default', ''), 
           help=option.get('description', ''), 
           action=option.get('action', 'store')) 

    return parser.parse_args() 

樣品myapp.py將是:

my_options = [ 
    { 
     "option": "-s", 
     "destination": "remote_host", 
     "default": "127.0.0.1", 
     "description": "The remote server name or IP address", 
     "action": "store" 
    }, 
] 

# Get Command Line Options 
options = get_options(misc_opts=my_options) 
print options.config 
print options.remote_host 

,這將被稱爲:

$> python myapp.py -c config.yaml 
$> config.yaml 
    127.0.0.1 

現在,我想創建一個單位測試這個功能,但我的問題是,我可以'通過測試代碼傳遞命令行參數。

# mytest.py 
import unittest 
from mymodule import get_options 

class argParseTestCase(unittest.TestCase): 
    def test_parser(self): 
     options = get_options() 
     # ...pass the command line arguments... 
     self.assertEquals('config.yaml', options.config) # ofcourse this fails because I don't know how I will pass the command line arguments 

我的問題是,我需要在命令行參數傳遞給get_options(),但我不知道如何正確地做到這一點。

預期正確撥打電話:(-c config.yaml應該測試代碼內部通過某種方式)python mytest.py

什麼是 「工作」/不是現在的工作:

  1. python mytest.py -c config.yaml也不能正常工作。返回AttributeError: 'module' object has no attribute 'config',因爲它期望我改爲撥打argParseTestCase。換句話說,python mytest.py -c argParseTestCase「作品」,但當然是返回AssertionError: 'config.yaml' != 'argParseTestCase'
  2. python mytest.py -v在詳細模式下運行單元測試也失敗。它返回:

    test_parser (main.argParseTestCase) ... mytest.py 1.0 ERROR ERROR: test_parser (main.argParseTestCase)
    Traceback (most recent call last): File "tests/unit_tests/mytest.py", line 376, in test_parser options = get_options() File "/root/test/lib/python2.7/site-packages/mymodule.py", line 61, in get_options return parser.parse_args()
    File "/usr/local/lib/python2.7/argparse.py", line 1701, in parse_args args, argv = self.parse_known_args(args, namespace)
    File "/usr/local/lib/python2.7/argparse.py", line 1733, in parse_known_args namespace, args = self._parse_known_args(args, namespace)
    File "/usr/local/lib/python2.7/argparse.py", line 1939, in _parse_known_args start_index = consume_optional(start_index)
    File "/usr/local/lib/python2.7/argparse.py", line 1879, in consume_optional take_action(action, args, option_string)
    File "/usr/local/lib/python2.7/argparse.py", line 1807, in take_action action(self, namespace, argument_values, option_string)
    File "/usr/local/lib/python2.7/argparse.py", line 1022, in call parser.exit(message=formatter.format_help())
    File "/usr/local/lib/python2.7/argparse.py", line 2362, in exit _sys.exit(status) SystemExit: 0

回答

2

你的錯誤信息堆棧是難以閱讀,因爲它是在引號形式,而不是代碼。但我認爲-v的說法是產生一個sys.exitversion就像help - 它應該顯示一條消息,然後退出。 unittest使用-v,但您的解析器也會讀取它。

有一個​​unittest模塊,test/test_argparse.py。您可能需要開發Python安裝才能看到。有些測試很簡單,有些則使用專門的測試結構。其中一些特殊代碼以與options相同的方式創建參數。

的兩個特殊問題:

  • 生成輸入。 parse_args使用sys.argv[1:],除非它的argv參數不是None。因此,您可以通過修改sys.argv列表(unittest已經使用您的命令行值)或將argv=None關鍵字參數傳遞到您的函數以及parse_args來測試解析器。試圖製作一個命令行,用於unittest代碼與get_options一起工作太複雜。

  • 陷印輸出,尤其是sys.exit產生的錯誤。一種選擇是將ArgumentParser分類並給它一個不同的error和/或exit方法。另一個是將函數調用包裝在try塊中。

unittest需要-c的說法,但有不同的語法和意義

-c, --catch  Catch control-C and display results 

-vverbose,不version

=============

此測試config參數(在一個自包含一個文件形式)

import unittest 
import sys 
#from mymodule import get_options 

def get_options(argv=None, prog_version='1.0', prog_usage='', misc_opts=None): 
    # argv is optional test list; uses sys.argv[1:] is not provided 
    from argparse import ArgumentParser 
    options = [] if misc_opts is None else misc_opts 
    parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser() 
    parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version)) 
    parser.add_argument('-c', '--config', dest='config', help='the path to the configuration file') 

    for option in options: 
     if 'option' in option and 'destination' in option: 
      parser.add_argument(option['option'], 
           dest=option.get('destination', ''), 
           default=option.get('default', ''), 
           help=option.get('description', ''), 
           action=option.get('action', 'store')) 

    args = parser.parse_args(argv) 
    print('args',args) 
    return args 

class argParseTestCase(unittest.TestCase): 
    def test_config(self): 
     sys.argv[1:]=['-c','config.yaml']  
     options = get_options() 
     self.assertEquals('config.yaml', options.config) 
    def test_version(self): 
     sys.argv[1:]=['-v'] 
     with self.assertRaises(SystemExit): 
        get_options() 
     # testing version message requires redirecting stdout 
    # similarly for a misc_opts test 

if __name__=='__main__': 
    unittest.main() 
3

我喜歡明確地傳遞參數,而不是依靠的在全球可用的屬性,如sys.argv(其中parser.parse_args()內部)。因此,我通常自己將列表傳遞的參數使用​​(以main(),隨後get_options()何地你需要他們):

def get_options(args, prog_version='1.0', prog_usage='', misc_opts=None): 
    # ... 
    return parser.parse_args(args) 

,然後傳入參數

def main(args): 
    get_options(args) 

if __name__ == "__main__": 
    main(sys.argv[1:]) 

這樣我可以代替並測試我喜歡的任何參數列表

options = get_options(['-c','config.yaml']) 
self.assertEquals('config.yaml', options.config)