如何测试我的代码是否抛出了适当的argparse异常?

时间:2016-11-30 21:41:48

标签: python unit-testing argparse

this great answer我学会了将参数解析放入自己的函数中以简化单元测试。

this answer我了解到,有时您需要抛出自己的解析器错误,以获得argparse来执行您想要的行为。 E.g:

if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')

但很难测试它是否能够实现它应该做的事情,因为抛出解析器错误也会退出程序。所以像这样的TestCase不会起作用:

def test_no_action_error(self):
    '''Test if no action produces correct error'''
    with self.assertRaises(ArgumentError) as cm:
        args = parse_args(' ')
    self.assertEqual('No action requested, add -process or -upload', str(cm.exception))

第一个问题的评论提出this question。但我不遵循如何在测试文件中使用此代码。

3 个答案:

答案 0 :(得分:3)

经过一番黑客攻击后,我发现了一些可以通过测试的东西。建议删除残酷的欢迎。

在我的主程序中,我使用一些额外的关键字arg定义了parse_args,仅用于测试。

def parse_args(args, prog = None, usage = None):
    PARSER = argparse.ArgumentParser(prog=prog, usage=usage)
    ....

然后在用于测试解析器的测试类中,添加这些参数以尽可能地抑制错误使用和帮助信息。

class ArgParseTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        self.testing_params = {'prog':'TESTING', 'usage':''}
        super(ArgParseTestCase, self).__init__(*args, **kwargs)

在测试文件中定义了this answer的上下文管理器:

from contextlib import contextmanager
from io import StringIO

@contextmanager
def capture_sys_output():
    capture_out, capture_err = StringIO(), StringIO()
    current_out, current_err = sys.stdout, sys.stderr
    try:
        sys.stdout, sys.stderr = capture_out, capture_err
        yield capture_out, capture_err
    finally:
        sys.stdout, sys.stderr = current_out, current_err

然后在上面的问题中将测试修改为:

def test_no_action_error(self):
    '''Test if no action produces correct error'''
    with self.assertRaises(SystemExit) as cm, capture_sys_output() as (stdout, stderr):
        args = parse_args([' '], **self.testing_params)
    self.assertEqual(2, cm.exception.code)
    self.assertEqual('usage: \n TESTING: error: No action requested, add -process or -upload',
                     stderr.getvalue())

现在assertEqual开头的额外文字并不漂亮......但测试通过,所以我很开心。

答案 1 :(得分:0)

<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <style type="text/css"> .hidden { display: none; } </style> </head> <body ng-app="myapp"> <div ng-controller="foo"> <div ng-if="!post.firstFeatured" class="col-sm-10 blog-content blogPreview" style="max-width: 400px"> <a ng-click="goToPostDetail(post, $index)"> <img class="img-responsive img-blog" ng-src="{{ post.fields.image.fields.file.url }}" width="100%" alt="" /> <div class="blogOverlay" ng-class="{'hidden' : !post.readMore}" ng-mouseover="showReadMore(post)" ng-mouseleave="hideReadmore(post)"> <h2>{{ post.fields.title }}</h2> </div> <div class="newOverlay" ng-class="{'hidden' : post.readMore}" ng-mouseleave="showReadMore(post)" ng-mouseover="hideReadmore(post)"> <h2>{{ post.fields.title }}</h2> <h3>{{post.fields.date}}</h3> <a class="btn btn-primary readmore" ng-click="goToPostDetail(post, $index)">Read More</a> </div> </a> </div> </div> </body> </html>进行了一些此类测试:

例如:

test/test_argparse.py

但关键是在文件开头附近定义的class TestArgumentTypeError(TestCase): def test_argument_type_error(self): def spam(string): raise argparse.ArgumentTypeError('spam!') parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) parser.add_argument('x', type=spam) with self.assertRaises(ArgumentParserError) as cm: parser.parse_args(['XXX']) self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n', cm.exception.stderr) 子类。

ErrorRaisingArgumentParser

有关详细信息,请参阅该文件。使用stderr重定向它会变得有点复杂。也许不仅仅是真正的需要。

答案 2 :(得分:0)

使用 pytest 的最简单方法如下:

with pytest.raises(SystemExit) as e:
    parse_args(...)

assert isinstance(e.value.__context__, argparse.ArgumentError)
assert 'expected err msg' in e.value.__context__.message

我们需要这种解决方法,因为 argparse 将退出错误代码 2,这意味着将引发 SystemExit