Python unittest测试DotaPlayer类时触发FileNotFoundError问题求解
问题原因
- 第一个错误是
FileNotFoundError,根源在logger_create函数的实现:- 日志文件路径依赖当前工作目录(
os.getcwd()),直接运行业务代码时工作目录通常是项目根目录,而运行unittest时工作目录默认是tests文件夹,路径计算后指向的位置和预期不符。 logging.FileHandler不会自动创建不存在的父级目录,只要路径中的log文件夹不存在,就会直接抛出找不到文件/目录的错误。
- 日志文件路径依赖当前工作目录(
- 第二个逻辑问题是
check_keywords的判断条件写反,你预期的是「缺少三个关键字中的任意一个就抛出SystemExit」,但现有代码的判断是if all(keys in response_json for keys in keywords),也就是三个关键字全部存在时才会进入报错分支,和预期逻辑完全相反,这也是你没拿到预期SystemExit的核心原因之一。
解决方案
第一步:修复日志创建逻辑
修改logger_create函数,自动创建父目录,同时使用固定的项目根目录计算路径,不要依赖动态的工作目录:
import os import logging def logger_create(): logger = logging.getLogger(__name__) if not logger.hasHandlers(): logger.setLevel(logging.INFO) formatter = logging.Formatter("%(levelname)s : %(asctime)s :: %(message)s") # 先获取当前文件所在目录,再往上找项目根目录,避免依赖os.getcwd() current_file_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(current_file_dir) log_dir = os.path.join(project_root, "log") # 自动创建log目录,不存在就新建 os.makedirs(log_dir, exist_ok=True) log_file_path = os.path.join(log_dir, "logger_new.log") file_handler = logging.FileHandler(filename=log_file_path) file_handler.setFormatter(formatter) logger.addHandler(file_handler) return logger
第二步:修复check_keywords的判断逻辑
把all改成not all,符合「只要有一个关键字不存在就报错」的业务逻辑:
@staticmethod def check_keywords(response_json): logger = logger_create() keywords = ("kills", "assists", "deaths") # 改成not all,判断是否存在缺失的关键字 if not all(keys in response_json for keys in keywords): msg = dict() msg["code"] = 2 msg["message"] = f"Player misses {keywords} for computing the KPI" logger.error(msg) sys.exit() else: return response_json
可选:测试时Mock日志避免依赖文件系统
如果不想让单元测试依赖本地目录和文件,可以在测试代码中Mock掉logger_create函数,直接返回模拟的日志对象,跳过文件写入逻辑:
import unittest from unittest.mock import patch, MagicMock from analyzer.etl import DotaPlayer class TestDotaPlayer(unittest.TestCase): def setUp(self) -> None: self.player = DotaPlayer(3) # mock logger_create,返回模拟的logger @patch("analyzer.etl.logger_create") def test_check_keywords(self, mock_logger): mock_logger.return_value = MagicMock() response_json = { "match_id": 6135452599, "player_slot": 2, "duration": 1926, "game_mode": 22, "lobby_type": 7, "hero_id": 129, "start_time": 1628968743, "skill": 2, "leaver_status": 4, "party_size": 3, } with self.assertRaises(SystemExit): self.player.check_keywords(response_json)
内容的提问来源于stack exchange,提问作者Sofiya Chobanyan
相关产品推荐
相关产品推荐

