Проблема заключается в следующем, я создал фиктивный пример. Где структура папок:
.
├── 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__)
Flask приложение:
# 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
Это происходит из-за регистрации чертежа. И я хотел понять магию c, которая происходит под капотом flask (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
? Заранее спасибо.