如何使用Mock编写单元测试验证调用第三方API的Flask接口
解决Flask接口单元测试中Mock第三方API的问题
我先帮你梳理下现有代码里的问题,然后给出正确的Mock实现方式:
第一步:修复Flask接口代码的逻辑问题
你的接口代码里有个明显的逻辑错误:return r.json()["currentDateTime"]之后的代码永远不会被执行,而且还误用了未定义的response变量。先把接口代码修正为:
import requests from flask import Flask, request, jsonify, make_response app = Flask(__name__, template_folder="templates") @app.route('/get_time', methods=['GET']) def get_time(): try: time_zone = request.args.get('time_zone') if not time_zone: # 新增:校验时区参数是否存在 return make_response(jsonify({"Error": "time_zone parameter is required"}), 400) url = f"http://worldclockapi.com/api/json/{time_zone}/now" r = requests.get(url) r.raise_for_status() # 触发HTTP错误的异常,比如404/500等 return r.json()["currentDateTime"] except requests.exceptions.RequestException as e: # 捕获请求相关的异常(比如连接失败、HTTP错误) return make_response(jsonify({"Error": str(e)}), 400) except Exception as e: # 捕获其他未知异常 return make_response(jsonify({"Error": "Unexpected error occurred"}), 500) if __name__ == '__main__': app.run(debug=True)
第二步:正确编写单元测试和Mock逻辑
你的测试代码存在几个问题:测试方法名不符合unittest规范(必须以test_开头)、没有使用Flask测试客户端调用接口、Mock路径不正确,还有未正确获取接口响应。下面是修正后的测试代码:
import json import unittest from unittest.mock import patch import requests from timeofday import app # 导入你的Flask应用实例 class MockResponse: def __init__(self, json_data, status_code): self.json_data = json_data self.status_code = status_code def json(self): return self.json_data def raise_for_status(self): # 模拟requests的raise_for_status方法,状态码异常时抛出错误 if self.status_code >= 400: raise requests.exceptions.HTTPError(f"HTTP Error {self.status_code}") def mock_requests_get_success(*args, **kwargs): # 模拟第三方API成功响应 return MockResponse( json_data={ "currentDateTime": "2019-11-08T15:52-05:00", "utcOffset": "-05:00:00", "isDayLightSavingsTime": False, "dayOfTheWeek": "Friday", "timeZoneName": "Eastern Standard Time" }, status_code=200 ) def mock_requests_get_failure(*args, **kwargs): # 模拟第三方API失败响应(比如404) return MockResponse( json_data={"Error": "Time zone not found"}, status_code=404 ) class TestGetTimeAPI(unittest.TestCase): def setUp(self): # 创建Flask测试客户端,开启测试模式 self.client = app.test_client() self.client.testing = True @patch('timeofday.requests.get') # 关键:Mock的是Flask文件中导入的requests.get def test_get_time_success(self, mock_get): # 配置Mock返回成功响应 mock_get.side_effect = mock_requests_get_success # 调用Flask接口 response = self.client.get('/get_time?time_zone=est') # 断言响应状态码和内容 self.assertEqual(response.status_code, 200) self.assertEqual(response.data.decode('utf-8'), "2019-11-08T15:52-05:00") # 断言第三方API被正确调用 mock_get.assert_called_once_with("http://worldclockapi.com/api/json/est/now") @patch('timeofday.requests.get') def test_get_time_missing_timezone(self, mock_get): # 调用接口时不传time_zone参数 response = self.client.get('/get_time') self.assertEqual(response.status_code, 400) self.assertIn("time_zone parameter is required", response.json["Error"]) # 此时第三方API不应该被调用 mock_get.assert_not_called() @patch('timeofday.requests.get') def test_get_time_api_failure(self, mock_get): # 配置Mock返回失败响应 mock_get.side_effect = mock_requests_get_failure response = self.client.get('/get_time?time_zone=invalid_zone') self.assertEqual(response.status_code, 400) self.assertIn("HTTP Error 404", response.json["Error"]) if __name__ == '__main__': unittest.main()
关键知识点说明
- Mock路径的正确性:当你在Flask文件(比如
timeofday.py)里导入requests并调用requests.get时,必须Mocktimeofday.requests.get,而不是全局的requests.get——unittest.mock的patch是基于模块的导入路径来替换对象的。 - 使用Flask测试客户端:通过
app.test_client()可以模拟HTTP请求调用接口,完整测试接口的逻辑流程,包括参数校验、异常处理等。 - 覆盖多场景测试:除了成功场景,还要测试参数缺失、第三方API失败等异常情况,确保接口的错误处理逻辑正常工作。
- 验证Mock调用:用
mock_get.assert_called_once_with(...)可以确认第三方API是否被传入正确的参数调用,保证接口URL拼接逻辑正确。
内容的提问来源于stack exchange,提问作者Fagner Schuindt Caetano
相关产品推荐
相关产品推荐

