如何使用Pytest测试需登录验证的Flask API端点?
我在测试自己开发的Flask API时,因为目标端点要求登录验证,导致测试失败。
我的测试代码如下:
import os import requests from dotenv import load_dotenv load_dotenv() ENDPOINT = os.getenv("ENDPOINT") ### - TESTING SALES ENDPOINT - ### def test_get_all_sales(): """Test the sales endpoint, where all sales are getted""" response = requests.get(f"http://localhost:5000/sales") print(response) assert response.status_code == 200
使用python -m pytest(Windows环境,Linux命令可能不同)运行测试时,返回403禁止访问错误,原因是该端点需要登录。
我的API采用Flask Session实现认证:前端向auth/login端点发送请求,后端存储会话并返回Cookie给前端保存。我想知道如何测试这类需要登录的端点?是否可以先完成登录、临时保存Cookie,再用该Cookie访问目标端点?或者是否需要为每个端点配置API Key,通过传入API Key绕过登录验证?
目标端点代码如下:
@sales.route('/') @sales.route("/<filters>") @login_required # abort and return 403. def sales_list(filters=None): sales = Sale.get_all_sales() # Sale是一个模型类,包含CRUD方法(简单DAO) if sales: return jsonify({'message': "sales fetched sucessfully.", 'data': sales}), 200 return jsonify({'message': "sales cant be fetched.", 'data': None}), 400
另外,我注意到POSTMAN可以设置Cookie并访问需登录的端点,所以认为Python也能手动实现这个功能。
解决方案
方法一:模拟登录获取Cookie并复用
这是最贴近真实前端交互的测试方式,步骤很简单:先登录拿到会话Cookie,再带着Cookie访问目标端点。
修改后的测试代码如下:
import os import requests from dotenv import load_dotenv load_dotenv() ENDPOINT = os.getenv("ENDPOINT") # 替换成你自己的测试账号信息 TEST_USER = {"username": "test_user", "password": "test_pass"} def test_get_all_sales(): """Test the sales endpoint with authenticated session""" # 1. 先调用登录接口获取会话Cookie login_response = requests.post(f"{ENDPOINT}/auth/login", json=TEST_USER) assert login_response.status_code == 200 # 先确保登录成功 # 2. 提取登录返回的Cookie,这个Cookie就是会话凭证 session_cookies = login_response.cookies # 3. 带着Cookie请求sales端点 response = requests.get(f"{ENDPOINT}/sales", cookies=session_cookies) print(response) assert response.status_code == 200
如果你的登录接口是表单提交(不是JSON格式),把json=TEST_USER改成data=TEST_USER就行。
方法二:为测试环境配置API Key绕过认证(可选)
要是觉得每次模拟登录太麻烦,可以给测试环境加个API Key的旁路逻辑,不用走登录流程直接访问。
先修改端点的认证逻辑:
import os from flask import request # 自定义一个装饰器,测试环境用API Key,生产环境走原登录验证 def api_key_required(f): def decorated_function(*args, **kwargs): if os.getenv("FLASK_ENV") == "testing": api_key = request.headers.get("X-API-Key") # 验证请求头里的API Key是否正确 if api_key == os.getenv("TEST_API_KEY"): return f(*args, **kwargs) # 非测试环境或者Key不对,还是走原来的登录验证 return login_required(f)(*args, **kwargs) return decorated_function # 把原来的@login_required换成自定义的装饰器 @sales.route('/') @sales.route("/<filters>") @api_key_required def sales_list(filters=None): sales = Sale.get_all_sales() if sales: return jsonify({'message': "sales fetched sucessfully.", 'data': sales}), 200 return jsonify({'message': "sales cant be fetched.", 'data': None}), 400
然后修改测试代码,请求时带上API Key:
def test_get_all_sales(): """Test the sales endpoint with API Key""" headers = {"X-API-Key": os.getenv("TEST_API_KEY")} response = requests.get(f"{ENDPOINT}/sales", headers=headers) print(response) assert response.status_code == 200
⚠️注意:这个方法只适合测试环境用,绝对不能在生产环境启用,会有安全风险。
方法三:用Flask测试客户端(更推荐)
如果是测试Flask应用本身,用Flask自带的测试客户端更高效,它能自动维护会话状态,不用手动处理Cookie:
import os from dotenv import load_dotenv from flask_testing import TestCase from your_app import create_app # 导入你项目里创建Flask应用的函数 load_dotenv() class TestSalesAPI(TestCase): def create_app(self): # 创建测试用的Flask实例,开启测试模式 app = create_app() app.config["TESTING"] = True return app def setUp(self): # 每个测试前先完成登录,测试客户端会自动保存会话 self.client = self.app.test_client() self.client.post("/auth/login", json={"username": "test_user", "password": "test_pass"}) def test_get_all_sales(self): # 直接用已登录的客户端访问端点 response = self.client.get("/sales") self.assertEqual(response.status_code, 200)
这种方式完全模拟了Flask内部的会话机制,适合写单元测试和集成测试,比手动处理Cookie更稳定。
内容的提问来源于stack exchange,提问作者vitxr

