使用unittest测试argparse-退出错误
问题内容:
要去关格雷格·哈斯在这个问题的答案,我试图让一个单元测试来检查当我通过它的一些ARGS不存在在该argparse是给相应的错误choices
。但是,unittest
使用以下try/except
语句会产生误报。
另外,当我仅使用一条with assertRaises
语句进行测试时,会argparse
强制系统退出,并且程序不再执行任何其他测试。
我希望能够对此进行测试,但是鉴于argparse
错误会退出,也许这是多余的?
#!/usr/bin/env python3
import argparse
import unittest
class sweep_test_case(unittest.TestCase):
"""Tests that the merParse class works correctly"""
def setUp(self):
self.parser=argparse.ArgumentParser()
self.parser.add_argument(
"-c", "--color",
type=str,
choices=["yellow", "blue"],
required=True)
def test_required_unknown_TE(self):
"""Try to perform sweep on something that isn't an option.
Should return an attribute error if it fails.
This test incorrectly shows that the test passed, even though that must
not be true."""
args = ["--color", "NADA"]
try:
self.assertRaises(argparse.ArgumentError, self.parser.parse_args(args))
except SystemExit:
print("should give a false positive pass")
def test_required_unknown(self):
"""Try to perform sweep on something that isn't an option.
Should return an attribute error if it fails.
This test incorrectly shows that the test passed, even though that must
not be true."""
args = ["--color", "NADA"]
with self.assertRaises(argparse.ArgumentError):
self.parser.parse_args(args)
if __name__ == '__main__':
unittest.main()
错误:
Usage: temp.py [-h] -c {yellow,blue}
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue')
E
usage: temp.py [-h] -c {yellow,blue}
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue')
should give a false positive pass
.
======================================================================
ERROR: test_required_unknown (__main__.sweep_test_case)
Try to perform sweep on something that isn't an option.
----------------------------------------------------------------------
Traceback (most recent call last): #(I deleted some lines)
File "/Users/darrin/anaconda/lib/python3.5/argparse.py", line 2310, in _check_value
raise ArgumentError(action, msg % args)
argparse.ArgumentError: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue')
During handling of the above exception, another exception occurred:
Traceback (most recent call last): #(I deleted some lines)
File "/anaconda/lib/python3.5/argparse.py", line 2372, in exit
_sys.exit(status)
SystemExit: 2
问题答案:
这里的窍门是抓住SystemExit
而不是ArgumentError
。这是您改写的测试以捕获SystemExit
:
#!/usr/bin/env python3
import argparse
import unittest
class SweepTestCase(unittest.TestCase):
"""Tests that the merParse class works correctly"""
def setUp(self):
self.parser=argparse.ArgumentParser()
self.parser.add_argument(
"-c", "--color",
type=str,
choices=["yellow", "blue"],
required=True)
def test_required_unknown(self):
""" Try to perform sweep on something that isn't an option. """
args = ["--color", "NADA"]
with self.assertRaises(SystemExit):
self.parser.parse_args(args)
if __name__ == '__main__':
unittest.main()
现在可以正常运行,并且测试通过:
$ python scratch.py
usage: scratch.py [-h] -c {yellow,blue}
scratch.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue')
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
但是,您可以看到正在打印使用情况消息,因此您的测试输出有些混乱。检查使用消息是否包含“无效选择”也可能很好。
您可以通过打补丁来做到这一点sys.stderr
:
#!/usr/bin/env python3
import argparse
import unittest
from io import StringIO
from unittest.mock import patch
class SweepTestCase(unittest.TestCase):
"""Tests that the merParse class works correctly"""
def setUp(self):
self.parser=argparse.ArgumentParser()
self.parser.add_argument(
"-c", "--color",
type=str,
choices=["yellow", "blue"],
required=True)
@patch('sys.stderr', new_callable=StringIO)
def test_required_unknown(self, mock_stderr):
""" Try to perform sweep on something that isn't an option. """
args = ["--color", "NADA"]
with self.assertRaises(SystemExit):
self.parser.parse_args(args)
self.assertRegexpMatches(mock_stderr.getvalue(), r"invalid choice")
if __name__ == '__main__':
unittest.main()
现在,您仅看到常规测试报告:
$ python scratch.py
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
对于pytest用户,这是不检查消息的等效项。
import argparse
import pytest
def test_required_unknown():
""" Try to perform sweep on something that isn't an option. """
parser=argparse.ArgumentParser()
parser.add_argument(
"-c", "--color",
type=str,
choices=["yellow", "blue"],
required=True)
args = ["--color", "NADA"]
with pytest.raises(SystemExit):
parser.parse_args(args)
Pytest默认情况下捕获stdout / stderr,因此它不会污染测试报告。
$ pytest scratch.py
================================== test session starts ===================================
platform linux -- Python 3.6.7, pytest-3.5.0, py-1.7.0, pluggy-0.6.0
rootdir: /home/don/.PyCharm2018.3/config/scratches, inifile:
collected 1 item
scratch.py . [100%]
================================ 1 passed in 0.01 seconds ================================
您还可以使用pytest检查stdout / stderr内容:
import argparse
import pytest
def test_required_unknown(capsys):
""" Try to perform sweep on something that isn't an option. """
parser=argparse.ArgumentParser()
parser.add_argument(
"-c", "--color",
type=str,
choices=["yellow", "blue"],
required=True)
args = ["--color", "NADA"]
with pytest.raises(SystemExit):
parser.parse_args(args)
stderr = capsys.readouterr().err
assert 'invalid choice' in stderr
和往常一样,我发现pytest更易于使用,但是您可以使它在任何一个中都能工作。