How to mock a list in Python?

kinORnirvana picture kinORnirvana · May 31, 2017 · Viewed 7.5k times · Source

For example, I have some code which tries to access a list:

def some_code():
    script_dir = os.path.dirname(sys.argv[0])

I need to mock sys.argv[0]. So I add @patch.object to my test:

import os
import sys

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
@mock.patch.object(sys, 'argv', mock.Mock(return_value=[THIS_DIR]))
def test_mytest():
    some_code()

But this doesn't work. Pytest raises error:

>     script_dir = os.path.dirname(sys.argv[0])
E     TypeError: 'Mock' object does not support indexing

What am I doing wrong?

Answer

Martijn Pieters picture Martijn Pieters · May 31, 2017

Don't use a Mock object to replace a list, just replace sys.argv with a different list. You can still use mock.patch to manage the replacing and undoing however:

@mock.patch.object(sys, 'argv', [THIS_DIR])

Do note that you probably want to pass in a filename, not the directory (unless your test directory is nested under the actual directory you wanted to be used).

Your mock configuration replaced sys.argv with a callable object, so only sys.argv() would have produced your [THIS_DIR] list.

Demo:

>>> import sys
>>> from unittest import mock
>>> sys.argv
['']
>>> with mock.patch.object(sys, 'argv', ['foo', 'bar', 'baz']):
...     sys.argv
...
['foo', 'bar', 'baz']
>>> sys.argv
['']