2013-08-20 124 views
1

调用时在一个文件中(比如parser.py)我有:argparse未能从单元测试测试

import argparse 

def parse_cmdline(cmdline=None): 
    parser = argparse.ArgumentParser() 
    parser.add_argument('--first-param',help="Does foo.") 
    parser.add_argument('--second-param',help="Does bar.") 

    if cmdline is not None: 
     args = parser.parse_args(cmdline) 
    else: 
     args = parser.parse_args() 

    return vars(args) 

if __name__=='__main__': 
    print parse_cmdline() 

果然,从它的工作原理从命令行调用,当给我差不多就是我期望:

$ ./parser.py --first-param 123 --second-param 456 
{'first_param': '123', 'second_param': '456'} 

但后来我想unittest它,所以我写了test_parser.py文件:

import unittest 
from parser import parse_cmdline 

class TestParser(unittest.TestCase): 
    def test_parse_cmdline(self): 
     parsed = parse_cmdline("--first-param 123 --second-param 456") 

     self.assertEqual(parsed['first_param'],'123') 
     self.assertEqual(parsed['second_param'],'456') 

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

然后我得到以下错误:

usage: test_parser.py [-h] [--first-param FIRST_PARAM] 
         [--second-param SECOND_PARAM] 
test_parser.py: error: unrecognized arguments: - - f i r s t - p a r a m 1 2 3 - - s e c o n d - p a r a m 4 5 6 
E 
====================================================================== 
ERROR: test_parse_cmdline (__main__.TestParser) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "./test_parser.py", line 8, in test_parse_cmdline 
    parsed = parse_cmdline("--first-param 123 --second-param 456") 
    File "/home/renan/test_argparse/parser.py", line 12, in parse_cmdline 
    args = parser.parse_args(cmdline) 
    File "/usr/lib/python2.7/argparse.py", line 1691, in parse_args 
    self.error(msg % ' '.join(argv)) 
    File "/usr/lib/python2.7/argparse.py", line 2361, in error 
    self.exit(2, _('%s: error: %s\n') % (self.prog, message)) 
    File "/usr/lib/python2.7/argparse.py", line 2349, in exit 
    _sys.exit(status) 
SystemExit: 2 

---------------------------------------------------------------------- 
Ran 1 test in 0.004s 

FAILED (errors=1) 

如可以看到的,我所指定的命令行(--first-param 123 --second-param 456)成为 - - f i r s t - p a r a m 1 2 3 - - s e c o n d - p a r a m 4 5 6(每个字符由空格隔开)。

我不明白为什么:我做错了什么?

回答

7

​​希望的“参数向量”,也就是说,单独的参数,而不是“命令行”的字符串列表。

而只是调用split解决不了的事情。例如,从shell命令行:

python script.py --first-param '123 456' --second-param 789 

...将正确给你123 456789,但此行的代码:

parse_cmdline("--first-param '123 456' --second-param 789") 

不会;它会给你'123789,还有一个额外的456',它不知道如何处理。

事实上,即便这是错误的:

parse_cmdline("--first-param '123' --second-param 789") 

...因为你会得到'123'而不是123


有两种方法可以解决这个问题。

首先,如果你知道你想传递什么,你可以将它作为一个列表而不是一个字符串放在第一位,而且你不需要担心繁琐的引用和分割细节:

parse_cmdline(["--first-param", "123 456", "--second-param", "789"]) 

另外,如果你不知道你想通过,你只知道它是什么样子的壳到底是什么,你可以使用shlex

if cmdline is not None: 
    args = parser.parse_args(shlex.split(cmdline)) 

...现在的Python将以与标准Unix shell相同的方式拆分命令行,获得123 456作为单个参数。

+0

基于shlex的解决方案对我来说已经足够好了。谢谢! – Renan

+1

尽管考虑'split'和'shlex'的区别是很好的,它本身并不测试'arpgarse'。 'test_argparse.py'使用'split'很多,但不会导入'shlex'。如果参数中的空白很重要,我只会将'parse_args'提供给一个测试单词列表。 – hpaulj

1

要回答我自己(我意识到我错了几分钟后):

我应该

if cmdline is not None: 
    args = parser.parse_args(cmdline.split()) 
else: 
    args = parser.parse_args() 

现在的测试正确传递!

+0

这是一个严重的问题。用'parse_cmdline(“ - first-param'123 456' - second-param 789”)'尝试。这不会做同样的事情,如果你从实际的shell中传递相同的东西。 – abarnert

+0

@abarnert我明白了;那么最好的解决方案是什么? – Renan

+1

这取决于你的确切用例。我已经写了一个解释两种标准解决方案的答案。 – abarnert