python-babel / flask-babel

i18n and l10n support for Flask based on Babel and pytz
https://python-babel.github.io/flask-babel/
Other
432 stars 159 forks source link

Change in unit test behavior when using test_client breaks makes tests less realistic #214

Open jdimmerman opened 1 year ago

jdimmerman commented 1 year ago

As of flask_babel 3.0.0, flask babel locale context is no longer reset between flask test_client requests. Consider the following unit test and config:

Test app has this locale_selector:

from flask import has_request_context, request

def locale_selector():
    if has_request_context():
        return request.accept_languages.best_match(LanguageType)
    return None

and then the test (assuming some basic flask app):

from flask_babel import get_locale

def test_get_locale(app):
    app.test_client.get(url_for("some_blueprint.some_route"), headers={"Accept-Language": "en"})
    assert str(get_locale()) == "en"

    app.test_client.get(url_for("some_blueprint.some_route"), headers={"Accept-Language": "es"})
    assert str(get_locale()) == "es"

This test succeeded in 2.x because get_locale() would call locale_selector on each test. In 3.0.0, the locale remains cached after the first request is made, causing the second assertion to fail.

To make the second test succeed, we can use refresh:

from flask_babel import get_locale, refresh

def test_get_locale(app):
    app.test_client.get(url_for("some_blueprint.some_route"), headers={"Accept-Language": "en"})
    assert str(get_locale()) == "en"

    refresh()

    app.test_client.get(url_for("some_blueprint.some_route"), headers={"Accept-Language": "es"})
    assert str(get_locale()) == "es"

However, this makes the tests more poorly reflect reality.

jdimmerman commented 1 year ago

This is because the ctx flask_babel now uses is a custom context stored on the flask global g and not the request context (https://github.com/python-babel/flask-babel/commit/3883ee78b3576a0bece3420dab812897638c2450). My tests were sharing flask global between requests so I had to add a custom before_request to clear to the tests.