如何在单元测试期间测试所需的 argparse 参数?

Posted

技术标签:

【中文标题】如何在单元测试期间测试所需的 argparse 参数?【英文标题】:How to test required argparse arguments during unittests? 【发布时间】:2019-11-15 23:16:08 【问题描述】:

我正在为 argparser 做 TDD 测试。如何使用 required 选项测试参数?我需要测试所有选项,例如:

参数过多, 没有给出任何参数, 给出了错误的参数。

我可以提出 SystemExit,但这并不是我真正需要的:

    def test_no_arguments(self):
        with patch.object(sys, 'exit') as mock_method:
            self.parser.parse_arguments()
            self.assertTrue(mock_method.called)

但是,在不引发系统退出的情况下,我总是会出现这样的错误:

zbx-check-mount.py

​​>
class CommandLine:
    def __init__(self):
        self.args_parser = argparse.ArgumentParser(description="Monitoring mounted filesystems",
                                                   formatter_class=argparse.RawTextHelpFormatter)
        self.parsed_args = None

        self.add_arguments()

    def add_arguments(self):
        """
        Add arguments to parser.
        """
        try:
            self.args_parser._action_groups.pop()  # pylint: disable=protected-access
            required = self.args_parser.add_argument_group('required arguments')
            required.add_argument('--fs_name', required=True, help='Given filesystem')
        except argparse.ArgumentError as err:
            log.error('argparse.ArgumentError: %s', err)
            sys.exit(1)

    def parse_arguments(self, args=None):
        """
        Parse added arguments. Then run private method to return values
        """
        self.parsed_args = self.args_parser.parse_args()

        return self.parsed_args.fs_name,

测试

from pyfakefs.fake_filesystem_unittest import TestCase
import os
import sys

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

if sys.version_info[0] == 3:
    from unittest.mock import MagicMock, patch
else:
    from mock import MagicMock, patch


sys.path.extend([os.path.join(os.path.dirname(os.path.abspath(__file__)),'..','..', "bin")])
module_name = __import__('zbx-check-mount')


class TestCommandLine(TestCase):

    def setUp(self):
        """
        Method called to prepare the test fixture. This is called immediately before calling the test method
        """
        self.parser = module_name.CommandLine()

    def test_no_arguments(self):
        opts = self.parser.parse_arguments([])
        assert opts.fs_name

    def tearDown(self):
        """
        Method called immediately after the test method has been called and the result recorded.
        """
        pass

如何避免这种情况并测试其他选项?

【问题讨论】:

【参考方案1】:

def parse_arguments(self, args=None): 中,您应该将args 传递给解析器,如下所示:

self.args_parser.parse_args(args)

parse_args() 解析sys.argv[1:],或者如果给定的参数是None。否则它会解析提供的列表。

python 的完整发行版中,有一个argparse (test_argparse.py) 的单元测试文件。这有点复杂,定义一个ArgumentParser 的子类来捕获错误并重定向错误消息。

测试argparse 很棘手,因为它会查看sys.argv,同时还使用unittest 脚本。它通常会尝试退出错误。这已经在许多 SO 问题中讨论过。

【讨论】:

这是一个非常有用且清晰的解释!在每次测试中我都错过了self.args_parser.parse_args(args)【参考方案2】:

如果我正确地解释了您的症状,那么您在测试工具中遇到了问题,因为您的猴子修补实现 sys.exit 实际上返回了,这是 argparse 库没有预料到的。

引入引发异常的side_effect,然后您可以在单元测试中捕获并验证该异常,可能足以解决问题。

【讨论】:

以上是关于如何在单元测试期间测试所需的 argparse 参数?的主要内容,如果未能解决你的问题,请参考以下文章

Scala / Gradle 项目...在哪里存储单元测试所需的超大文件?

如何运行单元测试来测试具有 argparse 属性的脚本?

Argparse:制作所需的标志

用于argparse的python单元测试

Argparse 单元测试:禁止显示帮助消息

带有 argparse 的 Python 单元测试