Python单元测试如何配置Mock让异常仅在被测函数调用时抛出
问题说明
编写Python单元测试测试Google Calendar API调用逻辑时,需要构造接口返回400的异常场景,要求仅当被测函数内部调用execute()方法时才抛出HttpError,进入异常捕获分支。
现有代码存在两个核心问题:
- 提前在测试准备阶段实例化了带400状态的HttpMock对应的service,会导致测试执行到实例化步骤就直接抛出异常,不会等到被测函数逻辑运行
- 将
execute()的return_value设置为lambda函数,调用execute()时只会返回lambda对象本身,不会执行lambda内部的异常逻辑,因此无法触发预期的400错误
解决方案
使用Mock对象的side_effect参数替代return_value实现异常触发:side_effect定义的内容会在Mock方法被实际调用时自动执行——如果传入异常类/实例,调用时直接抛出对应异常;如果传入可调用对象(比如lambda),调用时会自动执行该对象逻辑。
方式1:结合官方HttpMock触发真实400响应
@patch("add_entry_to_calendar.build") def test_add_entry_to_calendar_400(self, mock_build): # 初始化返回400状态的HttpMock http = HttpMock('tests/config-test.json', {'status': '400'}) # 获取patch生成的mock service实例,不需要提前调用真实build mock_service = mock_build.return_value # 给execute方法设置side_effect,调用execute时自动执行lambda触发400异常 # 注意lambda参数和实际调用保持一致,被测代码中execute无入参,所以lambda不加形参 mock_service.events.return_value.insert.return_value.execute.side_effect = \ lambda: build("calendar", "v3", http=http).events().insert(calendarId='primary').execute(http=http) # 调用被测函数,验证异常被捕获后返回None self.assertEqual(add_entry_to_calendar({"A":"B"}), None)
方式2:直接构造HttpError(更推荐,测试更稳定高效)
不需要依赖HttpMock和本地配置文件,直接构造异常实例即可,单元测试执行更快,没有外部依赖:
from googleapiclient.errors import HttpError from unittest.mock import Mock @patch("add_entry_to_calendar.build") def test_add_entry_to_calendar_400(self, mock_build): # 构造400状态的HttpError实例 mock_resp = Mock(status=400, reason="Bad Request") http_400_error = HttpError(resp=mock_resp, content=b'{"error": "invalid request"}') mock_service = mock_build.return_value # 直接将异常实例赋值给side_effect,调用execute时自动抛出该异常 mock_service.events.return_value.insert.return_value.execute.side_effect = http_400_error self.assertEqual(add_entry_to_calendar({"A":"B"}), None)
注意:不要在测试准备阶段手动调用
build("calendar", "v3", http=http)生成真实service,这行代码会在测试用例启动时就执行,直接抛出400异常,无法实现被测函数内部调用时才抛错的效果。
内容的提问来源于stack exchange,提问作者Dennis_M
相关产品推荐
相关产品推荐

