Suppose there is such a parser.
main.py
__version__ = '0.0.1'
def prepare_parser():
parser = ArgumentParser(description=(__doc__),
formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('-d', '--debug', action='store_true',
help='Show debug log')
parser.add_argument('-v', '--version', action='version',
version=__version__,
help='Show version and exit')
return parser
Suppose you want to test that this verison part can be used properly.
The trouble is that parser.parse_args (['-v'])
will cause SystemExit to fly at that point.
>>> from main import prepare_parser
>>> parser = prepare_parser()
>>> args = parser.parse_args(['-v'])
0.0.1
$ <--The interpreter has ended because the System Exit flies.
This is a specification of the argparse version action.
'version' - This expects a version= keyword argument in the add_argument() call, and prints version information and exits when invoked:
https://docs.python.org/3.6/library/argparse.html#action
I don't want sys.exit ()
to fly casually in the middle of a unit test. Also, I want to confirm that the version is output properly.
There could be a patch (https://docs.python.jp/3/library/unittest.mock-examples.html#patch-decorators), but if possible I would like to treat the contents of the argparse as a black box.
Try the following.
tests.py
from contextlib import contextmanager
from io import StringIO
import unittest
import sys
from main import prepare_parser, __version__
@contextmanager
def capture_stdout(command, *args, **kwargs):
out, sys.stdout = sys.stdout, StringIO()
try:
command(*args, **kwargs)
finally:
sys.stdout.seek(0)
yield sys.stdout.read()
sys.stdout = out
class MyTest(unittest.TestCase):
def test_version(self):
parser = prepare_parser()
with self.assertRaises(SystemExit):
with capture_stdout(parser.parse_args, ['-v']) as output:
self.assertIsNotNone(output)
self.assertEqual(__version__, output.rstrip())
with self.assertRaises(SystemExit):
with capture_stdout(parser.parse_args,
['--version']) as output:
self.assertIsNotNone(output)
self.assertEqual(__version__, output.rstrip())
if __name__ == '__main__':
unittest.main()
By having the context manager yield the string that was sent to stdout when the exception was blown, hoping that SystemExit
would be skipped.
I came to be able to test.
When commenting out the optional part that parses -v
etc.
$ python tests.py
usage: tests.py [-h] [-d]
tests.py: error: unrecognized arguments: -v
F
======================================================================
FAIL: test_version (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 26, in test_version
self.assertEqual(__version__, output.rstrip())
AssertionError: '0.0.1' != ''
- 0.0.1
+
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
If you comment in
$ python tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
I don't think it's the kind of test I want to do. Is this test vulnerable ...? I'm not sure.
Is there a better way?
Recommended Posts