如何在使用 argparse 的文件上使用指定的测试目录运行 pytest?

Posted

技术标签:

【中文标题】如何在使用 argparse 的文件上使用指定的测试目录运行 pytest?【英文标题】:How to run pytest with a specified test directory on a file that uses argparse? 【发布时间】:2019-01-13 13:23:51 【问题描述】: 我正在使用 pytest 为 Python 库编写单元测试 我需要为测试文件指定一个目录以避免自动测试文件发现,因为有一个很大的子目录结构,包括库中包含“_test”或“test_”的许多文件在名称中,但不适用于 pytest 库中的某些文件使用 argparse 来指定命令行选项 问题在于将 pytest 的目录指定为命令行参数似乎会干扰使用 argparse 的命令行选项

举个例子,我在根目录下有个文件叫script_with_args.py,如下:

import argparse

def parse_args():
    parser = argparse.ArgumentParser(description="description")

    parser.add_argument("--a", type=int, default=3)
    parser.add_argument("--b", type=int, default=5)

    return parser.parse_args()

我在根目录中还有一个名为 tests 的文件夹,其中包含一个名为 test_file.py 的测试文件:

import script_with_args

def test_script_func():
    args = script_with_args.parse_args()
    assert args.a == 3

如果我从命令行调用python -m pytest,则测试通过。如果我在命令行中用python -m pytest tests指定测试目录,会返回如下错误:

============================= test session starts =============================
platform win32 -- Python 3.6.5, pytest-3.5.1, py-1.5.3, pluggy-0.6.0
rootdir: C:\Users\Jake\CBAS\pytest-tests, inifile:
plugins: remotedata-0.2.1, openfiles-0.3.0, doctestplus-0.1.3, arraydiff-0.2
collected 1 item

tests\test_file.py F                                                     [100%]

================================== FAILURES ===================================
______________________________ test_script_func _______________________________

    def test_script_func():
        # a = 1
        # b = 2
>       args = script_with_args.parse_args()

tests\test_file.py:13:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
script_with_args.py:9: in parse_args
    return parser.parse_args()
..\..\Anaconda3\lib\argparse.py:1733: in parse_args
    self.error(msg % ' '.join(argv))
..\..\Anaconda3\lib\argparse.py:2389: in error
    self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = ArgumentParser(prog='pytest.py', usage=None, description='description', f
ormatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_h
elp=True)
status = 2, message = 'pytest.py: error: unrecognized arguments: tests\n'

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
>       _sys.exit(status)
E       SystemExit: 2

..\..\Anaconda3\lib\argparse.py:2376: SystemExit
---------------------------- Captured stderr call -----------------------------
usage: pytest.py [-h] [--a A] [--b B]
pytest.py: error: unrecognized arguments: tests
========================== 1 failed in 0.19 seconds ===========================

我的问题是,如何在不干扰 argparse 的命令行选项的情况下为 pytest 指定测试文件目录?

【问题讨论】:

您的问题不是如何避免在测试会话中传递 CLI args,而是如何为 CLI args 解析逻辑编写适当的测试。您有两种可能性:模拟 sys.argv 列表或 ArgumentParser.parse_args 返回值,或重构您的 parse_args 函数,使其接受 args 列表。两种方式在链接问题的答案中都有示例。 How do you write tests for the argparse portion of a python module?的可能重复 @hoefling 或者***.com/questions/18668947/… 【参考方案1】:

parse_args() 不带参数读取sys.argv[1:] 列表。这将包括“测试”字符串。

pytests 也将 sys.argv[1:] 与它自己的解析器一起使用。

使您的解析器可测试的一种方法是提供可选的argv

def parse_args(argv=None):
    parser = argparse.ArgumentParser(description="description")

    parser.add_argument("--a", type=int, default=3)
    parser.add_argument("--b", type=int, default=5)

    return parser.parse_args(argv)

然后您可以使用以下方法对其进行测试:

parse_args(['-a', '4'])

并与

一起真正使用它
parse_args()

更改sys.argv 也是一个好方法。但是,如果您打算将解析器放入这样的函数中,您不妨赋予它更多的灵活性。

【讨论】:

在尝试为 pytest 测试注入命令行参数时,这对我不起作用。 Unittest.mock 本身可以工作,但无法将命令行参数更改为我以编程方式注入的值。【参考方案2】:

要添加到 hpaulj 的答案,您还可以使用像 unittest.mock 这样的库来临时屏蔽 sys.argv 的值。这样,您的 parse args 命令将使用“模拟”argv 运行,但 actual sys.argv 保持不变。

当您的测试调用 parse_args() 时,他们可以这样做:

with unittest.mock.patch('sys.argv', ['--a', '1', '--b', 2]):
    parse_args()

【讨论】:

【参考方案3】:

我在 VS Code 中遇到了类似的测试发现问题。 VS Code 中的运行适配器传入了我的程序不理解的参数。我的解决方案是让解析器接受未知参数。

变化:

return parser.parse_args()

收件人:

args, _ = parser.parse_known_args()
return args

【讨论】:

以上是关于如何在使用 argparse 的文件上使用指定的测试目录运行 pytest?的主要内容,如果未能解决你的问题,请参考以下文章

使用不需要指定输出文件名的可选参数运行 argparse

如何在 argparse 中使用带有 nargs='*' 参数的可选位置参数?

argparse 模块如何在没有任何参数的情况下添加选项?

如何在Python中实现argparse

如何使用 2 个位置参数在 argparse 上打印帮助界面?

如何在 python 中使用 argparse 和 json 文件