Python单元测试是一种复杂的方法



我正在寻找关于如何对以下方法进行单元测试的任何建议。

def delete_old_files(self):
"""Deletes all recordings older then a predefined time period.
Checks the mtime of files in _store_dir and if it is older then the predefined time period
delete them.
"""
now = time.time()
self.logger.info(
f"Delete: Looking for files older then {self.config.delete.older_then}"
f" days in: {self._store_dir}."
)
try:
for root, dirs, files in os.walk(self._store_dir):
for file in files:
try:
if file == ".gitignore":
continue
file = os.path.join(root, file)
# check if the mtime is older then x days (x * hours * minutes * seconds)
if os.stat(file).st_mtime < now - (
self.config.delete.older_then * 24 * 60 * 60
):
if os.path.isfile(file):
os.remove(file)
self.logger.info(f"Delete:t{file}")
except FileNotFoundError:
self.logger.warning(
f'Could not find "{file}". Continue without deleting this file.'
)
except PermissionError:
self.logger.warning(
f'Permission denied when trying to delete "{file}".'
" Continue without deleting this file."
)
# TODO: Do we want to handle the exception here or higher up?
# Should we treat this as unacceptable or not?
except FileNotFoundError:
self.logger.warning(
f"Directory not found! Could not find {self._store_dir}"
" Continue without deleting old files."
)
except PermissionError:
self.logger.warning(
f"Permission denied! Could not access {self._store_dir}"
" Continue without deleting old files."
)

一点背景信息:我正在写一个录音机,它将所有文件保存到self._store_dir中,我想测试的方法是一个可选的垃圾收集器。应删除早于self.config.delete.older_then的文件。任何关于如何对这样的方法进行单元测试或重构以使其更容易测试的建议都是非常受欢迎的。

我的想法是:

  • 模拟文件系统,并提供了所有不同的情况。但这与单元测试应该做的相反,对吧
  • 把这个方法分成更小的方法,然后自己测试

我自己想明白了。我重构了我的代码,这样它就不会在做它所做的事情所需的信息上撒谎。然后我添加了一个返回值,我可以测试是否出现故障或一切顺利。我发现了一个关于python中单元测试的很棒的博客,这对我帮助很大。

为了完整起见,这里是我重构的代码:

def delete_old_files(self, store_dir: str, older_then: int) -> bool:
"""Deletes all recordings older then a predefined time period.
Checks the mtime of files in store_dir and if it is older then the predefined time period,
delete them.
Args:
store_dir: The file path as a string to the directory to check for files to delete.
older_then: The minimum age of the files in days which will be deleted.
Returns:
True, if the method could delete all files older than the minimum age.
This is also True if no files were found and therefore no files were deleted.
False, if the method encounters an FileNotFoundError or PermissionError
for at least one file.
"""
now = time.time()
self.logger.info(
f"Delete: Looking for files older then {older_then}" f" days in: {store_dir}."
)
no_deletion_problems = True
for root, dirs, files in os.walk(store_dir):
for file in files:
try:
if file == ".gitignore":
continue
file = os.path.join(root, file)
# check if the mtime is older then x days (x * hours * minutes * seconds)
if os.stat(file).st_mtime < now - (older_then * 24 * 60 * 60):
os.remove(file)
self.logger.info(f"Delete:t{file}")
except FileNotFoundError:
self.logger.warning(
f'Could not find "{file}". Continue without deleting this file.'
)
no_deletion_problems = False
except PermissionError:
self.logger.warning(
f'Permission denied when trying to delete "{file}".'
" Continue without deleting this file."
)
no_deletion_problems = False
return no_deletion_problems

如果它能帮助任何人,我也会发布我的测试:

def test_delete_old_files(self):
"""Test to delete old files."""
raw_dict = {"name": "log_test", "file": None, "level": "INFO"}
log_config = recorder.Config.Logger(raw_dict)
log_handler = recorder.LogHandler(log_config)
logger = log_handler.get_logger()
self.recorder.logger = logger
with mock.patch("os.walk") as mock_oserror:
mock_oserror.return_value = [("root", "dirs", ["file1, file2", ".gitignore"])]
self.assertFalse(self.recorder.delete_old_files("dir_to_check", 1))
class stat_obj:
"""Dummy stat class."""
st_mtime = 0
@patch.multiple(
"os",
walk=MagicMock(return_value=[("root", "dirs", ["file1, file2", ".gitignore"])]),
stat=MagicMock(return_value=stat_obj()),
remove=MagicMock(return_value=None),
)
def test_delete_old_files_mocking_os_stat(self, **mocks):
"""Test to delte old files when mocking os.stat()."""
raw_dict = {"name": "log_test", "file": None, "level": "INFO"}
log_config = recorder.Config.Logger(raw_dict)
log_handler = recorder.LogHandler(log_config)
logger = log_handler.get_logger()
self.recorder.logger = logger
self.assertTrue(self.recorder.delete_old_files("dir_to_check", 1))
@patch.multiple(
"os",
walk=MagicMock(return_value=[("root", "dirs", ["file1, file2", ".gitignore"])]),
stat=MagicMock(return_value=stat_obj()),
remove=MagicMock(side_effect=PermissionError),
)
def test_delete_old_files_mocking_permission_error(self, **mocks):
"""Test to delte old files when mocking for a permission error."""
raw_dict = {"name": "log_test", "file": None, "level": "INFO"}
log_config = recorder.Config.Logger(raw_dict)
log_handler = recorder.LogHandler(log_config)
logger = log_handler.get_logger()
self.recorder.logger = logger
self.assertFalse(self.recorder.delete_old_files("dir_to_check", 1))

我继续嘲讽,并在代码中进一步增加了一些不必要的检查。总的来说,这并不完美,但对我来说已经足够好了。

相关内容

最新更新