0

I'm doing TDD tests for argparser. How can I test arguments with the option required?I need to test all options like:

  • too many arguments,
  • no arguments are given,
  • the wrong argument is given.

I can raise SystemExit, but this is not really what I need:

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

However, without raising system exit I have always errors like this: test_error

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,

tests

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

How to avoid this situation and test other options?

2 Answers 2

1

In def parse_arguments(self, args=None):, you should pass args on to the parser, as in:

self.args_parser.parse_args(args)

parse_args() parses sys.argv[1:], or if the given argument is None. Otherwise it parses the provided list.

In a full distribution of python there's a unittest file for argparse (test_argparse.py). It's somewhat complex, defining a subclass of ArgumentParser that captures errors and redirects error messages.

Testing argparse is tricky because it looks at sys.argv, with the unittest scripts also use. And it usually tries to exit on errors. This has been discussed in a number of SO questions already.

Sign up to request clarification or add additional context in comments.

1 Comment

It was a very useful and clear explanation! In every test I missed self.args_parser.parse_args(args).
1

If I'm interpreting your symptoms correctly, you are having problems in the test harness because your monkey patched implementation of sys.exit actually returns, which the argparse library is not expecting.

Introducing a side_effect that raises an exception, which you can then trap and verify in the unit test, may be sufficient to get around the problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.