I'm late posting here, but I wanted to share my solution (based on this answer), and also include my mock
ed unit tests.
I made a function to create a path if it doesn't exist, like mkdir -p
(and called it mkdir_p
to facilitate my remembering it).
my_module.py
import os
import errno
def mkdir_p(path):
try:
print("Creating directory at '{}'".format(path))
os.makedirs(path)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(path):
print("Directory already exists at '{}'".format(path))
else:
raise
If os.makedirs
is unable to create the directory, we check the OSError
error number. If it is errno.EEXIST
(==17), and we see that the path exists, we don't need to do anything (although printing something could be helpful). If the error number is something else, e.g. errno.EPERM
(==13), then we raise the exception, because the directory is not available.
I'm testing it by mocking os
and assigning error numbers to OSError
in the test functions. (This uses a file tests/context.py to allow easy importing from the parent directory, as suggested by Kenneth Reitz. Although not directly related to the question, I'm including it here for the sake of completeness.)
tests/context.py
import sys
import os
sys.path.insert(0, os.path.abspath('..'))
import my_module
tests/my_module_tests.py
import errno
import unittest
import mock
from .context import my_module
@mock.patch('my_module.os')
class MkdirPTests(unittest.TestCase):
def test_with_valid_non_existing_dir(self, mock_os):
my_module.mkdir_p('not_a_dir')
mock_os.makedirs.assert_called_once_with('not_a_dir')
def test_with_existing_dir(self, mock_os):
mock_os.makedirs.side_effect = OSError(errno.EEXIST, 'Directory exists.')
mock_os.path.isdir.return_value = True
my_module.mkdir_p('existing_dir')
mock_os.path.isdir.assert_called_once_with('existing_dir')
def test_with_permissions_error(self, mock_os):
mock_os.makedirs.side_effect = OSError(errno.EPERM, 'You shall not pass!')
with self.assertRaises(OSError):
my_module.mkdir_p('forbidden_dir')