mnprkk
mnprkk

Reputation: 41

Flask Pytest UserWarning: The setup method 'route' can no longer be called on the blueprint

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

Answers (4)

atharva
atharva

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

Tomer Gal
Tomer Gal

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

TRezendes
TRezendes

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

Be Hai Nguyen
Be Hai Nguyen

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(
  • tests/unit/test_user.py has 22 tests.
  • tests/unit/test_unit_of_measure.py has 5 tests.

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

Related Questions