python - pytest`AssertionError: View function mapping is overwritten an existing endpoint function:`flask-restful while registring blueprint
问题描述
问题如下,我创建了一个虚拟示例。文件夹结构在哪里:
.
├── api_bp
│ └── __init__.py
├── app.py
├── pytest.ini
└── tests
├── conftest.py
├── __init__.py
├── test_todo1.py
└── test_todo2.py
文件夹api_bp
里面的代码__init__.py
:
# __init__.py
from flask import Blueprint
api_bp = Blueprint('api', __name__)
烧瓶应用程序:
# app.py
from flask import Flask, Blueprint
from flask_restful import Api, Resource
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
def create_app():
"""Initialize the app. """
app = Flask(__name__)
from api_bp import api_bp
# api_bp = Blueprint('api', __name__)
api = Api(api_bp)
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp, url_prefix='/api')
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
出于测试目的,我为不同的待办事项提供了客户端夹具和两个测试(我故意将它们放入单独的模块中):
# conftest.py
import pytest
from app_factory import create_app
@pytest.fixture(scope='module')
def client():
flask_app = create_app()
testing_client = flask_app.test_client()
context = flask_app.app_context()
context.push()
yield testing_client
context.pop()
# test_todo1.py
import pytest
def test_todo2(client):
"""Test"""
response = client.get('/api/todos/1')
print(response)
assert response.status_code == 200
# test_todo2.py
import pytest
def test_todo2(client):
"""Test"""
response = client.get('/api/todos/2')
print(response)
assert response.status_code == 200
因此,当我运行$ pytest -v
测试它时,我最终会遇到以下错误:
AssertionError: View function mapping is overwriting an existing endpoint function: api.todoitem
这是因为注册了蓝图。我想了解烧瓶 (flask-restful) 与 pytest 相结合的神奇之处。因为如果我像这样定义我的app.py
模块,它会成功通过测试:
# app.py
from flask import Flask, Blueprint
from flask_restful import Api, Resource
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
def create_app():
"""Initialize the app. """
app = Flask(__name__)
# note: I commented the line below and defined the blueprint in-place
# from api_bp import api_bp
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp, url_prefix='/api')
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
$ pytest -v
tests/test_api1.py::test_todo2 PASSED [ 50%]
tests/test_api2.py::test_todo2 PASSED [100%]
或者,如果我不使用应用程序工厂,它也可以正常工作:
# app.py
from flask import Flask, Blueprint
from flask_restful import Api, Resource
app = Flask(__name__)
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp, url_prefix='/api')
如果我将所有测试都放在一个模块中,或者我先注册蓝图然后添加如下资源,也可以修复它:
# app.py
...
def create_app():
"""Initialize the app. """
app = Flask(__name__)
from api_bp import api_bp
api = Api(api_bp)
app.register_blueprint(api_bp, url_prefix='/api')
api.add_resource(TodoItem, '/todos/<int:id>')
return app
...
谁知道这里发生了什么并能解释一下magic
?提前致谢。
解决方案
因此,问题的解释是,当 pytest 设置并在测试中使用客户端时,它会运行create_app()
并在未在内部定义蓝图时尝试重用蓝图app.py
:
tests/test_api1.py::test_todo2 <flask.blueprints.Blueprint object at 0x7f04a8c9c610>
SETUP M client
tests/test_api1.py::test_todo2 (fixtures used: client)<Response streamed [200 OK]>
PASSED
TEARDOWN M client
tests/test_api2.py::test_todo2 <flask.blueprints.Blueprint object at 0x7f04a8c9c610>
SETUP M clientERROR
TEARDOWN M client
它可以通过这样做来修复:
# api_bp/__init__.py
from flask import Blueprint
get_blueprint = lambda: Blueprint('api', __name__)
并使用:
def create_app():
"""Initialize the app. """
app = Flask(__name__)
from api_bp import get_blueprint
api_bp = get_blueprint()
api = Api(api_bp)
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp, url_prefix='/api')
return app
因此,此类问题的最简单解决方案是使用适当的 pytest 范围(不是“模块”):
@pytest.fixture(scope='session')
def client():
...
更新:
这种方法不适用于定义管理命令,例如:
class Test(Command):
def run(self):
"""Runs the tests."""
pytest.main(['-s', '-v', './tests'])
manager.add_command('test', Test) # run the tests
使用python app.py test
您将得到与前面示例中相同的错误。有关更多详细信息,请阅读以下链接中的“注意:”部分:https ://docs.pytest.org/en/latest/usage.html#calling-pytest-from-python-code
推荐阅读
- amazon-web-services - 使用 for_each 和 ARN 列表输出的 Terraform 模块调用
- firebase - 无法将数据从 firestore 检索到 listview 扑扑
- javascript - 如何减少对象数组
- android - 在 Android 10(及更高版本)上,如何以编程方式检测哪个应用程序在外部/辅助屏幕的前台运行?
- javascript - 如何为购物车制作数量变化功能?
- python - 如何从 PYCHARM 中找到正确的 python 解释器并在 cmd 中使用相同的解释器?
- javascript - for循环中的随机延迟setTimeout
- autodesk-forge - 有没有办法在查看器中使用 BoxSelection 扩展启用部分选择
- java - 您可以垂直打印生成的随机数(10 个整数)一维数组吗?
- python - Pytest 测试套件在 GitHub Actions 上随机出现错误,无需更改