[ Python: Returning tuple for os.walk in unittest ]
I am writing a unit test for the following function:
def create_zip(path,zipf):
#Create zip files
for root, dirs, files in os.walk(path):
for file in files:
#write all json files to a zip file
for name in glob.glob('*.json'):
os.chdir(root)
#write files to zip file
zipf.write(name)
#Remove files after creating zip file
os.remove(name)
This is what I have at the moment:
tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")
@mock.patch('my_script.os.walk')
def test_create_basic_zip(self,mock_os_walk):
mock_os_walk.return_value = ('test1','test2','test3')
zipf = zipfile.ZipFile(self.tmpfilepath, 'w', zipfile.ZIP_DEFLATED)
files = my_script.create_zip('.',zipf)
However I keep getting the error:
for root, dirs, files in os.walk(path):
ValueError: too many values to unpack
or when I change it to:
mock_os_walk.return_value = (['test1'],['test2'],['test3'])
I get the error:
ValueError: need more than 1 value to unpack
Where am I going wrong?
Answer 1
In order to match the os.walk()
result format, you need to have an iterable where each item consists of 3 elements - the root
, dirs
and files
. Replace:
mock_os_walk.return_value = ('test1','test2','test3')
with, for example:
mock_os_walk.return_value = [('./test1', ['test2', 'test3'], ['test4.txt']), ]
Answer 2
Your mock of os.walk needs to return an iterable, since you are doing for ... in os.walk(...):
It turns out that a tuple is an iterable. So when you set ...mock.return_value = (...)
you are defining the iterable that will be returned.
You call os.walk
, and get a tuple back. But you called it in the context of a for
loop, so effectively you have written:
for a,b,c in ('test1', 'test2', 'test3'):
Which expands to:
a,b,c = 'test1'
the first time through the loop.
What you really want is to provide an iterable that gives a value for each of the variables, at least once and possibly more. This code works, and I think you can figure out the rest:
#!python3
from unittest import mock
import os
def other_function():
for a,b,c in os.walk('.'):
print(a,b,c)
@mock.patch('os.walk')
def test_code(mock_os_walk):
mock_os_walk.return_value = ((1,2,3), (4,5,6), (7,8,9))
other_function()
test_code()