20

I have a main() function in python that gets command line arguments. Is there a way for me to write pytest tests for this function and define the arguments in the code?

e.g.

def main():
    # argparse code
    args, other = arg_parser.parse_known_args()
    return args.first_arg


def test_main():
    res = main() # call with first_arg = "abc"
    assert(res == "abc")

4 Answers 4

20

To add to the previous answers, instead of modifying sys.argv It is safer to use a context manager which can cover up and protect the underlying object. An example would be

with unittest.mock.patch('sys.argv', ['program_name', '--option1', 'inputFile']):
    main()

This works only with python3. For python2 the Mock library does the trick.

I found this solution on a different stackoverflow post here.

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

1 Comment

The context manager is a great addition. You do need to add one extra first string to the list, because the first string in sys.argv gets ignored by argparse. So something like ['junk', '--option1', 'inputFile'].
8

parse_args takes a argv parameter. The docs uses this repeatedly in it's examples

parser = argparse.ArgumentParser()
parser.add_argument('--foo', action='store_true')
parser.add_argument('bar')
parser.parse_known_args(['--foo', '--badger', 'BAR', 'spam'])

where the string list replicates sys.argv[1:] that it would get from the commandline. If the argument is None (or omitted) the parser uses sys.argv[1:].

So if

def main(argv=None):
    # argparse code
    args, other = arg_parser.parse_known_args(argv)
    return args.first_arg

You could test with

main(['foo', '-f','v'])

The unittesting file for argparse.py uses both this approach, and your's of modifying sys.argv directly.

https://docs.python.org/3/library/argparse.html#beyond-sys-argv

https://docs.python.org/3/library/argparse.html#partial-parsing

1 Comment

How does your solution play with a pytest test?
5

The best solution I found so far is this

def test_main():
    sys.argv = ["some_name", "abc"]
    res = main()

and for flags:

sys.argv.append("-f")
sys.argv.append("v")

Comments

0

For an entrypoint.py this might be particularly simple to add an extra argument. In this case --disable-warnings

import sys

import pytest


if __name__ == "__main__":
    exit_code = pytest.main(sys.argv[1:] + ["--disable-warnings"])

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.