如何设置sys.argv以便对其进行单元测试?

ftr*_*ers 50 python

我想设置

sys.argv
Run Code Online (Sandbox Code Playgroud)

所以我可以用不同的组合进行单元测试.以下不起作用:

#!/usr/bin/env python
import argparse, sys
def test_parse_args():
    global sys.argv
    sys.argv = ["prog", "-f", "/home/fenton/project/setup.py"]
    setup = get_setup_file()
    assert setup == "/home/fenton/project/setup.py"
def get_setup_file():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f')
    args = parser.parse_args()
    return args.file
if __name__ == '__main__':
    test_parse_args()
Run Code Online (Sandbox Code Playgroud)

然后运行文件:

pscripts % ./test.py                                                                                           
  File "./test.py", line 4
    global sys.argv
              ^
SyntaxError: invalid syntax
pscripts %  
Run Code Online (Sandbox Code Playgroud)

Jas*_*man 54

在运行时更改sys.argv是一种非常脆弱的测试方法.您应该使用mock补丁功能,它可以用作上下文管理器,在给定的代码块中将一个对象(或属性,方法,函数等)替换为另一个对象.

以下示例用于patch()有效地"替换" sys.argv指定的返回值(testargs).

try:
    # python 3.4+ should use builtin unittest.mock not mock package
    from unittest.mock import patch
except ImportError:
    from mock import patch

def test_parse_args():
    testargs = ["prog", "-f", "/home/fenton/project/setup.py"]
    with patch.object(sys, 'argv', testargs):
        setup = get_setup_file()
        assert setup == "/home/fenton/project/setup.py"
Run Code Online (Sandbox Code Playgroud)

  • 要使用标准库做同样的事情,语法将是`with unittest.mock.patch('sys.argv'.[stuff])` (8认同)
  • @JasonGoal 我认为他们输错了逗号 (`,`) (3认同)

ick*_*fay 8

global仅公开全局变量的模块内,并且sys.argv是在sys,不是你的模块.而不是使用global sys.argv,使用import sys.

但是,您可以完全避免更改sys.argv:只需get_setup_file选择一个参数列表(默认为None)并将其传递给parse_args.当get_setup_file被称为不带参数,这样的说法会None,并且parse_args将回落到sys.argv.当使用列表调用它时,它将用作程序参数.

  • @ftravers:但是[它*确实*参数](http://docs.python.org/3.3/library/argparse.html#argparse.ArgumentParser.parse_args).如果你查看文档中的所有示例,几乎*all*都会传递`parse_args`列表. (3认同)
  • @ftravers:我应该注意你的`if`声明不是必需的; `parse_args`*本身*将处理`None`并将其替换为`sys.argv`,如果是的话. (2认同)

xjc*_*jcl 7

我喜欢用unittest.mock.patch(). 不同之处在于patch.object(),您不需要直接引用要修补的对象,而是使用字符串。

from unittest.mock import patch

with patch("sys.argv", ["file.py", "-h"]):
    print(sys.argv)
Run Code Online (Sandbox Code Playgroud)


hpa*_*ulj 5

test_argparse.py,官方的argparseunittest文件,使用几种设置/使用方法argv

parser.parse_args(args)
Run Code Online (Sandbox Code Playgroud)

其中args是“单词”的列表,例如['--foo','test']--foo test'.split()

old_sys_argv = sys.argv
sys.argv = [old_sys_argv[0]] + args
try:
    return parser.parse_args()
finally:
    sys.argv = old_sys_argv
Run Code Online (Sandbox Code Playgroud)

这将args推到sys.argv

我刚刚遇到一个案例(使用mutually_exclusive_groups),该案例['--foo','test']产生的行为不同于'--foo test'.split()。这是一个涉及id诸如的字符串的微妙之处test

  • 就我个人而言,我喜欢只提供“parse_args()”参数列表并完全将 sys.argv 排除在外的方法。似乎是最干净的方式。 (2认同)

Ian*_*sco 1

它不起作用,因为你实际上并没有打电话get_setup_file。您的代码应为:

import argparse

def test_parse_args():
    sys.argv = ["prog", "-f", "/home/fenton/project/setup.py"]
    setup = get_setup_file()  # << You need the parentheses
    assert setup == "/home/fenton/project/setup.py"
Run Code Online (Sandbox Code Playgroud)