带有 argparse 的 Python 单元测试

Posted

技术标签:

【中文标题】带有 argparse 的 Python 单元测试【英文标题】:Python unittest with argparse 【发布时间】:2018-09-25 01:17:24 【问题描述】:

我正在尝试将 unittest 与使用 argparse 模块的程序一起使用,但遇到了一些困难。我引用了this helpful post 作为起点,但我显然仍然缺少一些东西。

这是基本程序:

#arg_test.py
import sys
import argparse


class Thingy:
    def __init__(self, name):
        self.name = name

    def parse_args(args):
        parser = argparse.ArgumentParser(description='description here')
        parser.add_argument('-v', '--version', action='version', version='%(prog)s 0.1')
        parser.add_argument('-a', '--arg1', required=True, help='this is for arg1')
        parser.add_argument('-b', '--arg2', required=True, help='this is for arg2')
        return parser.parse_args()


def main():
    parser = Thingy.parse_args(sys.argv[1:])
    print('the args are: '.format(parser))
    if parser.arg1:
        print('the value of arg1 is : '.format(parser.arg1))
    if parser.arg2:
        print('the value of arg2 is : '.format(parser.arg2))


if __name__ == '__main__':
    main()

运行这个:

python arg_test.py --arg1 asdf --arg2 qwer

导致预期输出:

the args are: Namespace(arg1='asdf', arg2='qwer')
the value of arg1 is : asdf
the value of arg2 is : qwer

现在是简单的单元测试程序:

#test/test_arg_test.py
import unittest
from arg_test import Thingy


def test_parser(self):
    parser = Thingy.parse_args(['--arg1'])
    self.assertTrue(parser.arg1,'asdf')


if __name__ == '__main__':
    main()

运行这个:

python -m unittest -v test/test_arg_test.py --arg1 asdf --arg2 qwer

结果如下:

python -m unittest -v test/test_arg_test.py --arg1 asdf --arg2 qwer
usage: python -m unittest [-h] [-v] [-q] [--locals] [-f] [-c] [-b]
                          [tests [tests ...]]
python -m unittest: error: unrecognized arguments: --arg1 asdf --arg2 qwer

有人可以为我指出如何运行这些测试的正确方向吗?

谢谢。

更新 #1

这是基于以下有用建议的更新单元测试程序,但仍然缺少一些内容。

#import unittest
from arg_test import Thingy

class TestThingys(unittest.TestCase):
    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_parser(self):
        argv1 = ['--arg1', 'asdf', '--arg2', 'qwer']
        parser = Thingy().parse_args(argv1)
        self.assertTrue(parser.arg1,'asdf')

if __name__ == '__main__':
    main()

运行这个:

python -m unittest -v test/test_arg_test.py

结果如下:

test_isupper (test.test_arg_test.TestThingys) ... ok
test_parser (test.test_arg_test.TestThingys) ... ERROR

======================================================================
ERROR: test_parser (test.test_arg_test.TestThingys)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/username/scripts/python/arg_test/test/test_arg_test.py", line 12, in test_parser
    parser = Thingy().parse_args(argv1)
TypeError: __init__() missing 1 required positional argument: 'name'

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (errors=1)

更新 #2

#test/test_arg_test.py
import unittest
from arg_test import Thingy

class TestThingys(unittest.TestCase):
    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_parser(self):
        argv1 = ['--arg1', 'asdf', '--arg2', 'qwer']
        parser = Thingy('name').parse_args(argv1)
        self.assertEquals(parser.arg1,'asdf')

if __name__ == '__main__':
    main()

运行这个:

python -m unittest -v test/test_arg_test.py

结果如下:

test_isupper (test.test_arg_test.TestThingys) ... ok
test_parser (test.test_arg_test.TestThingys) ... ERROR

======================================================================
ERROR: test_parser (test.test_arg_test.TestThingys)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/username/scripts/python/arg_test/test/test_arg_test.py", line 12, in test_parser
    parser = Thingy('name').parse_args(argv1)
TypeError: parse_args() takes 1 positional argument but 2 were given

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (errors=1)

【问题讨论】:

unittest 运行自己的解析器,它无法识别您的参数。您看到的是它的用法,而不是您的。 此外,您已经为测试测试中指定了参数;你不需要把它们放在命令行上。 需要进行 2 处更改,Thingy() 将变为 Thingy('name')self.assertTrue(parser.arg1,'asdf') 将替换为 self.assertEquals() 这不是你问题的答案,但我认为你不应该直接测试 args 解析(假设它有效,因为它是独立的经过良好测试的模块)并且不要直接在你的代码中使用它.要存根 args 对象,请使用 NamedTuple(或等效类型) @R2RT,测试的原因是确保已使用的选项不会在没有警告的情况下更改。目的不是为了测试argparse 【参考方案1】:

class TestThingy中使用argv

def test_parser(self):
    argv1 = ['--arg1', 'asdf', '--arg2', 'qwer']
    parser = Thingy('name').parse_args(argv1)
    self.assertEquals(parser.arg1,'asdf')

    argv2 = ['--trigger_exception`, 'asdf`]
    with self.assertRaise(Exception):
        parser = Thingy('name').parse_args(argv2)

【讨论】:

感谢您的反馈。我根据您的建议更新了代码,测试并将结果放在上面,请您看看吗? @user9074332,我的错误,我更新了我的代码并修复了错误,谢谢

以上是关于带有 argparse 的 Python 单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何在单元测试中使用 argparse 参数调用函数?

用于argparse的python单元测试

带有命令行参数的单元测试

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

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

为多个 Python 文件编写单元测试