使用create_app工厂模式的Flask+pytest测试出现404错误排查
问题:Flask测试环境使用自定义配置时接口返回404
我正在开发基于客户端-服务器架构的小型Flask应用,在通过pytest配置测试环境以使用预设数据库时遇到问题。项目结构如下:
| config.py | Pipfile | +---client +---mobile_app +---server | | database.py | | server.py | +---tests | | test_database.py | | test_server.py | | __init__.py
server.py 内容:
from flask import Flask from flask_restful import Api, Resource def create_app(config_obj): app = Flask(__name__) app.config.from_object(config_obj) return app app = create_app('config.DefaultConfig') api = Api(app) class Balance(Resource): @staticmethod def get(user_id): balance = 0 * user_id return {"balance": balance} api.add_resource(Balance, "/<int:user_id>/balance") if __name__ == "__main__": app.run(debug=True)
config.py 内容:
class DefaultConfig: DEBUG = True TESTING = False PROPAGATE_EXCEPTIONS = None SECRET_KEY = 'mysecretkey' PERMANENT_SESSION_LIFETIME = '31 days, 0: 00:00' USE_X_SENDFILE = False SERVER_NAME = None APPLICATION_ROOT = '/' SESSION_COOKIE_NAME = 'session' SESSION_COOKIE_DOMAIN = None SESSION_COOKIE_PATH = None SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SECURE = False SESSION_COOKIE_SAMESITE = None SESSION_REFRESH_EACH_REQUEST = True MAX_CONTENT_LENGTH = None SEND_FILE_MAX_AGE_DEFAULT = None TRAP_BAD_REQUEST_ERRORS = None TRAP_HTTP_EXCEPTIONS = False EXPLAIN_TEMPLATE_LOADING = False PREFERRED_URL_SCHEME = 'http' TEMPLATES_AUTO_RELOAD = None MAX_COOKIE_SIZE = 4093 class TestingConfig: DEBUG = True TESTING = True PROPAGATE_EXCEPTIONS = True SECRET_KEY = 'mysecretkey' PERMANENT_SESSION_LIFETIME = '31 days, 0: 00:00' USE_X_SENDFILE = False SERVER_NAME = None APPLICATION_ROOT = '/' SESSION_COOKIE_NAME = 'session' SESSION_COOKIE_DOMAIN = None SESSION_COOKIE_PATH = None SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SECURE = False SESSION_COOKIE_SAMESITE = None SESSION_REFRESH_EACH_REQUEST = True MAX_CONTENT_LENGTH = None SEND_FILE_MAX_AGE_DEFAULT = None TRAP_BAD_REQUEST_ERRORS = None TRAP_HTTP_EXCEPTIONS = False EXPLAIN_TEMPLATE_LOADING = False PREFERRED_URL_SCHEME = 'http' TEMPLATES_AUTO_RELOAD = None MAX_COOKIE_SIZE = 4093
test_server.py 内容:
import pytest from server.server import create_app, app @pytest.fixture() def client_not_working(): client = create_app('config.TestingConfig') with client.test_client() as testing_client: yield testing_client @pytest.fixture() def client_working(): yield app.test_client() fixture = client_not_working def test_get_balance(fixture): response = fixture.get('/1/balance') assert response.status_code == 200 assert response.headers['Content-Type'] == 'application/json' assert b'balance' in response.data assert response.json["balance"] == 0
当fixture设为client_working时测试可正常通过,但设为用于自定义配置的client_not_working时,访问/1/balance接口返回404错误,断言失败。请问哪里操作有误?
原因及解决方法
问题根源
client_not_working fixture调用create_app生成了全新的Flask实例,但这个新实例没有注册任何Flask-RESTful路由。原来的server.py中,Api初始化和路由注册逻辑是写在全局的,只对最开始创建的app实例生效,create_app函数内部没有这部分代码,导致新生成的测试用app完全没有接口路由,自然返回404。
而client_working fixture用的是全局已经完成路由注册的app实例,所以能正常访问接口。
解决方法(推荐应用工厂模式)
把Api和路由的注册逻辑移到create_app函数内部,让每个通过该函数创建的app实例都自带正确的路由配置:
修改后的server.py:
from flask import Flask from flask_restful import Api, Resource def create_app(config_obj): app = Flask(__name__) app.config.from_object(config_obj) # 将Api和路由注册逻辑移入函数内 api = Api(app) class Balance(Resource): @staticmethod def get(user_id): balance = 0 * user_id return {"balance": balance} api.add_resource(Balance, "/<int:user_id>/balance") return app # 全局app仍用默认配置初始化 app = create_app('config.DefaultConfig') if __name__ == "__main__": app.run(debug=True)
这样修改后,client_not_working fixture生成的测试app会自动包含Balance接口的路由,测试就能正常通过了。
内容的提问来源于stack exchange,提问作者kubablo
相关产品推荐
相关产品推荐

