Reputation: 41
I am relatively new to Flask and testing in general, and I would like to write tests for my routes. The one dummy test I have is passing, but with 3 warnings and I could not figure out why.
The warnings are:
tag_a_bird_backend/tests/test_auth.py::test_admin
/Users/kinga/Documents/tag-a-bird/backend/tag_a_bird_backend/app.py:106: UserWarning: The setup method 'route' can no longer be called on the blueprint 'route_blueprint'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
@route_blueprint.route('/api/admin', methods=['GET'])
tag_a_bird_backend/tests/test_auth.py::test_admin
/Users/kinga/Library/Caches/pypoetry/virtualenvs/tag-a-bird-backend-dX-qXFvi-py3.10/lib/python3.10/site-packages/flask/scaffold.py:449: UserWarning: The setup method 'add_url_rule' can no longer be called on the blueprint 'route_blueprint'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
self.add_url_rule(rule, endpoint, f, **options)
tag_a_bird_backend/tests/test_auth.py::test_admin
/Users/kinga/Library/Caches/pypoetry/virtualenvs/tag-a-bird-backend-dX-qXFvi-py3.10/lib/python3.10/site-packages/flask/blueprints.py:490: UserWarning: The setup method 'record' can no longer be called on the blueprint 'route_blueprint'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
self.record(
I have the following file structure:
├── __init__.py
├── app.py
├── tests
│ ├── conftest.py
│ └── test_auth.py
backend/__init__.py
from flask import Flask
from dotenv import load_dotenv
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
from tag_a_bird_backend.app import route_blueprint
app.register_blueprint(route_blueprint)
load_dotenv()
app.config.from_prefixed_env()
return app
backend/app.py
from flask_login import LoginManager, login_user, login_required, logout_user, current_user
from . import create_app, auth
from flask import Blueprint
route_blueprint = Blueprint('route_blueprint', __name__)
app = create_app()
login_manager = LoginManager(app)
@route_blueprint.route('/api/admin', methods=['GET'])
@auth.login_required
def about():
return 'hello admin'
if __name__ == '__main__':
app.run()
backend/tests/conftest.py
import pytest
from tag_a_bird_backend import create_app
@pytest.fixture
def app():
app = create_app({
"TESTING": True,
})
yield app
@pytest.fixture
def client(app):
yield app.test_client()
@pytest.fixture
def runner(app):
return app.test_cli_runner()
backend/tests/test_auth.py
def test_admin(client):
assert client.get('/api/admin').status_code == 401
Could anyone point me to what I might be doing wrong?
I have tried resting the routes with and without Blueprints, so I am open to that option too. I just would like to stick to the app factory pattern because it looks cleaner (?)
Upvotes: 3
Views: 5222
Reputation: 90
In my case, just adding a scope="module" solved the problem. My test functions were calling the app fixture multiple times, which raised the error. Making sure the app is only generated once fixes this.
@pytest.fixture(scope="module")
def app():
app = create_app()
app.config.update(
{
"TESTING": True,
}
)
yield app
Upvotes: 0
Reputation: 969
Registering blueprints inside create_app
in app context solved it for me
# main.py
from blueprints import *
...
def create_app():
with app.app_context():
app.register_blueprint(my_blueprint)
...
return app
Upvotes: 0
Reputation: 108
The fix is to register the Blueprints within an explicit app context.
from flask import Flask
from dotenv import load_dotenv
from flask_httpauth import HTTPBasicAuth
…
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
#Use `with app.app_context()` within the `create_app` definition.
with app.app_context():
from tag_a_bird_backend.app import route_blueprint
app.register_blueprint(route_blueprint)
…
return app
Although the Flask 2.2.0 change log makes it clear that
Use [of] Blueprint decorators and functions intended for setup after registering the blueprint will show a warning. In the next version, this will become an error just like the application setup methods.
I was unclear how to actually avoid the warning.
I struggled for days to find a solution to the UserWarning: The setup method 'route' can no longer be called on the blueprint 'route_blueprint'. It has already been registered at least once, any changes will not be applied consistently. Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
warning. I finally inadvertently solved it while implementing the fix for a different problem.
Because of a change to the way Flask 2.2.x & Flask-SQLAlchemy 3.3.x handle the session, I was also having trouble reflecting my database in models.py. Pushing an app context within models.py worked to reflect my DB, but created a circular import error. Thanks to davidism over on GitHub for the fix for that problem which also fixed the blueprint registration problem as a happy side effect.
Upvotes: 1
Reputation: 67
I suffer the same problem. After some experimentations, I choose to ignore these warnings till it becomes a real problem.
Under tests/unit/ I have 183 tests. In full run, these two appear:
tests/unit/test_user.py: 1 warning
f:\my_project\src\my_project\__init__.py:78: UserWarning: The setup method 'add_url_rule' can no longer be called on the blueprint 'auths'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
url[ 2 ].add_url_rule( url[0], methods=url[1], view_func=url[3] )
tests/unit/test_unit_of_measure.py: 1 warning
F:\my_project\venv\lib\site-packages\flask\blueprints.py:490: UserWarning: The setup method 'record' can no longer be called on the blueprint 'systems'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
self.record(
I commented out 20 tests in tests/unit/test_user.py, I had this new warning for tests/unit/test_unit_of_measure.py:
tests/unit/test_unit_of_measure.py: 1 warning
f:\my_project\src\my_project\__init__.py:78: UserWarning: The setup method 'add_url_rule' can no longer be called on the blueprint 'auths'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
url[ 2 ].add_url_rule( url[0], methods=url[1], view_func=url[3] )
I then proceeded to comment out another four tests from tests/unit/test_unit_of_measure.py. I had a completely new one:
tests/unit/test_timesheet_entry.py: 1 warning
F:\my_project\venv\lib\site-packages\flask\blueprints.py:490: UserWarning: The setup method 'record' can no longer be called on the blueprint 'auths'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.
This warning will become an exception in Flask 2.3.
self.record(
If I run them individually, I have none of this blueprint warning:
(venv) F:\my_project>venv\Scripts\pytest.exe -m unit_of_measure
================================================= test session starts =================================================
platform win32 -- Python 3.10.1, pytest-7.1.2, pluggy-1.0.0
rootdir: F:\my_project, configfile: pytest.ini
collected 392 items / 387 deselected / 5 selected
tests\unit\test_unit_of_measure.py ..... [100%]
================================================== warnings summary ===================================================
<frozen importlib._bootstrap>:283
<frozen importlib._bootstrap>:283: DeprecationWarning: the load_module() method is deprecated and slated for removal in Python 3.12; use exec_module() instead
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
==================================== 5 passed, 387 deselected, 1 warning in 0.98s =====================================
(venv) F:\my_project>venv\Scripts\pytest.exe -m user
================================================= test session starts =================================================
platform win32 -- Python 3.10.1, pytest-7.1.2, pluggy-1.0.0
rootdir: F:\my_project, configfile: pytest.ini
collected 392 items / 370 deselected / 22 selected
tests\unit\test_user.py ...................... [100%]
================================================== warnings summary ===================================================
<frozen importlib._bootstrap>:283
<frozen importlib._bootstrap>:283: DeprecationWarning: the load_module() method is deprecated and slated for removal in Python 3.12; use exec_module() instead
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
==================================== 22 passed, 370 deselected, 1 warning in 1.78s ====================================
These warnings don't yet give me any problems. I will keep my eyes though.
Upvotes: 1