问题
I have the following python code which I want to test:
def find_or_make_logfolder(self):
if not path.isdir(self.logfolder):
try:
makedirs(self.logfolder)
except OSError:
if not path.isdir(self.logfolder):
raise
I want to do something like the following in my unittest.
def test_find_or_make_logfolder_pre_existing(self):
with self.assertRaises(OSError):
makedirs(self.logfolder)
find_or_make_logfolder()
However, if not path.isdir(self.logfolder):
is checking if the directory already exists or not so that the except OSError
will only be thrown in some edge case where a program or person succeeds in making the directory a few milliseconds after the if
and before the try
.
How do I test this, or do I really need to test this?
I tend to like it when coverage says 100%.
回答1:
You could go a more Pythonic route to achieve this. In Python the philosophy is
It's better to ask for forgiveness than to ask for permission.
See EAFP here
With that in mind, your code could be written as follows:
def find_or_make_logfolder(self):
try:
makedirs(self.logfolder)
except OSError:
#self.logfolder was already a directory, continue on.
pass
Now to get 100% of this code covered, you will just need to create a test case where the directory already exists.
回答2:
mock library is a must-have tool for achieving a 100% coverage.
Mock out make_dirs()
function and set a side_effect on it:
side_effect allows you to perform side effects, including raising an exception when a mock is called
from mock import patch # or from unittest import mock for python-3.x
@patch('my_module.makedirs')
def test_find_or_make_logfolder_pre_existing(self, makedirs_mock):
makedirs_mock.side_effect = OSError('Some error was thrown')
with self.assertRaises(OSError):
makedirs(self.logfolder)
回答3:
There are plenty of other situations that raise OSError, e.g. filesystem full, insufficient permissions, file already exists etc.
In this case permissions is an easy one to exploit - simply arrange for self.logfolder
to be set to a nonexistent directory for which your process does not have write permission, e.g. in *nix assuming that you do not have write permission in the root directory:
>>> import os
>>> os.makedirs('/somedir')
OSError: [Errno 13] Permission denied: '/somedir'
Also, consider Martin Konecny's suggested refactor.
回答4:
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')
来源:https://stackoverflow.com/questions/23947312/how-do-i-write-a-unit-test-for-oserror