如何为OSError编写单元测试?

时间:2014-05-30 04:56:55

标签: python unit-testing testing code-coverage python-unittest

我有以下要测试的python代码:

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

我想在我的单元测试中执行以下操作。

def test_find_or_make_logfolder_pre_existing(self):
    with self.assertRaises(OSError):
        makedirs(self.logfolder)
        find_or_make_logfolder()

但是,if not path.isdir(self.logfolder):正在检查目录是否已经存在,以致except OSError仅在某个边缘情况下抛出,在这种情况下,程序或人员成功地使目录在几毫秒之后生成if之前和try之前。

我该如何测试,或者我真的需要测试吗?

当覆盖率100%时,我倾向于喜欢它。

4 个答案:

答案 0 :(得分:2)

你可以采用更多的Pythonic路线来实现这一目标。在Python中,哲学是

  

要求宽恕比要求许可更好。

请参阅EAFP here

考虑到这一点,您的代码可以写成如下:

def find_or_make_logfolder(self):
    try:
        makedirs(self.logfolder)
    except OSError:
        #self.logfolder was already a directory, continue on.
        pass

现在要覆盖100%的代码,您只需要创建一个目录已经存在的测试用例。

答案 1 :(得分:2)

mock库是实现100%覆盖率的必备工具。

模拟make_dirs()函数并在其上设置side_effect

  

side_effect允许你执行副作用,包括提高   调用mock时的异常

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)

答案 2 :(得分:0)

还有很多其他情况会引发OSError,例如:文件系统已满,权限不足,文件已存在等。

在这种情况下,权限很容易被利用 - 只需安排将self.logfolder设置为您的进程没有写入权限的不存在的目录,例如:在* nix中,假设您在根目录中没有写权限:

>>> import os
>>> os.makedirs('/somedir')
OSError: [Errno 13] Permission denied: '/somedir'

另外,请考虑Martin Konecny建议的重构。

答案 3 :(得分:0)

我迟到了,但我想分享我的解决方案(基于this answer),还包括我的mock ed单元测试。

我创建了一个函数来创建一个路径,如果它不存在,比如mkdir -p(并称之为mkdir_p以便于我记住它。)

<强> 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

如果os.makedirs无法创建目录,我们会检查OSError错误号。如果它是errno.EEXIST(== 17),并且我们看到路径存在,我们不需要做任何事情(虽然打印一些东西可能会有所帮助)。如果错误号是其他的,例如errno.EPERM(== 13),然后我们引发异常,因为该目录不可用。

我正在通过模拟os并在测试函数中为OSError指定错误编号来测试它。 (这使用文件 tests / context.py ,允许从父目录轻松导入,如Kenneth Reitz所示。虽然与问题没有直接关系,但我在此处将其包括在内为了完整起见。)

<强>测试/ context.py

import sys
import os
sys.path.insert(0, os.path.abspath('..'))

import my_module

<强>测试/ 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')