How do I set sys.argv so I can unit test it?

Solution 1:

Changing sys.argv at runtime is a pretty fragile way of testing. You should use mock's patch functionality, which can be used as a context manager to substitute one object (or attribute, method, function, etc.) with another, within a given block of code.

The following example uses patch() to effectively "replace" sys.argv with the specified return value (testargs).

try:
    # python 3.4+ should use builtin unittest.mock not mock package
    from unittest.mock import patch
except ImportError:
    from mock import patch

def test_parse_args():
    testargs = ["prog", "-f", "/home/fenton/project/setup.py"]
    with patch.object(sys, 'argv', testargs):
        setup = get_setup_file()
        assert setup == "/home/fenton/project/setup.py"

Solution 2:

test_argparse.py, the official argparse unittest file, uses several means of setting/using argv:

parser.parse_args(args)

where args is a list of 'words', e.g. ['--foo','test'] or --foo test'.split().

old_sys_argv = sys.argv
sys.argv = [old_sys_argv[0]] + args
try:
    return parser.parse_args()
finally:
    sys.argv = old_sys_argv

This pushes the args onto sys.argv.

I just came across a case (using mutually_exclusive_groups) where ['--foo','test'] produces different behavior than '--foo test'.split(). It's a subtle point involving the id of strings like test.