2013-08-20 155 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