烧瓶测试无法通过appcontext_pushed.connected_to设置flask.g

时间:2020-03-05 13:36:39

标签: python-3.x flask pytest

我的代码是错误的。所以问题结束了。非常感谢tmt。

===================起源问题⬇️================

我尝试按照flask-doc测试我的烧瓶应用程序(python3.8.1)

flask-doc

我想在测试前请求为我的应用设置flask.g,所以我使用 appcontext_pushed.connected_to 函数,但是它不起作用...

我在这里构建了一个小演示。您可以尝试将其复制到demo.py

pip安装烧瓶,料桶,pytest

pytest demo.py

您将收到AttributeError:'_AppCtxGlobals'对象没有属性'name'.......

为什么...

from contextlib import contextmanager
from flask import appcontext_pushed, g, json, jsonify, Flask


app = Flask(__name__)


def fetch_current_user_from_database():
    return {'name': 'aa'}


def get_user():
    user = getattr(g, 'user', None)
    if user is None:
        user = fetch_current_user_from_database()
        g.name = user['name']
    return user


@app.route('/users/me')
def users_me():
    return jsonify(username=g.name)


@contextmanager
def user_set(app, user):
    def handler(sender, **kwargs):
        g.user = user
    with appcontext_pushed.connected_to(handler, app):
        yield


def test_1():
    username = '123'
    with user_set(app, username):
        with app.test_client() as c:
            resp = c.get('/users/me')
            data = json.loads(resp.data)
            assert data['username'] == username

3 个答案:

答案 0 :(得分:0)

您已经从文档中覆盖了示例的某些部分,因此在调用g.nameusers_me()不存在。在您的示例中,fetch_current_user_from_database()get_user()似乎无关紧要。您没有测试它们,它们也永远不会被调用。您的测试似乎唯一要测试的是users_me()返回用户名。

因此,在测试中,您应该设置整个用户对象。这样的对象取决于您的应用程序,因为这不是Flask特定的。鉴于您尚未提供此类信息,我们只使用一个模拟对象:

from unittest.mock import MagicMock

from contextlib import contextmanager
from flask import appcontext_pushed, g, json, jsonify, Flask


app = Flask(__name__)

@app.route('/users/me')
def users_me():
    return jsonify(username=g.user.name)


@contextmanager
def user_set(app, user):
    def handler(sender, **kwargs):
        g.user = user

    with appcontext_pushed.connected_to(handler, app):
        yield

def test_1():
    user = MagicMock()
    user.name = 'aa'

    with user_set(app, user):
        with app.test_client() as c:
            resp = c.get('/users/me')
            data = json.loads(resp.data)
            assert data['username'] == user.name

答案 1 :(得分:0)

正确的代码是:

from contextlib import contextmanager
from flask import appcontext_pushed, g, json, jsonify, Flask


app = Flask(__name__)


@app.route('/users/me')
def users_me():
    return jsonify(username=g.user)


@contextmanager
def user_set(app, user):
    def handler(sender, **kwargs):
        g.user = user
    with appcontext_pushed.connected_to(handler, app):
        yield


def test_1():
    user = '123'
    with user_set(app, user):
        with app.test_client() as c:
            resp = c.get('/users/me')
            data = json.loads(resp.data)
            assert data['username'] == user

就像我说的那样是一个演示。在我的真实应用中,我想将用户对象发送到由下面的函数创建的g。

def login(client, email, password):
    print(f'login_info test start')
    ret = client.post('/api/login', json=dict(
        email=email,
        password=password
    ), follow_redirects=True)
    assert ret.status_code == 200
    data = json.loads(ret.data)
    uid = data['uid']
    customer = CustomerService.get(uid)
    print(f'login test end {customer.email}')
    assert customer

您可能会注意到该函数没有返回值。所以我得到一个None并将其发送给g。但是我没注意到那晚。

flask-doc的演示不完整且含糊不清:

with user_set(app, my_user):
    with app.test_client() as c:
        resp = c.get('/users/me')
        data = json.loads(resp.data)
        self.assert_equal(data['username'], my_user.username)

如果将应用更改为flaskr,我可能会更有勇气尝试。

with user_set(flaskr, my_user):
    with app.test_client() as c:
        resp = c.get('/users/me')
        data = json.loads(resp.data)
        self.assert_equal(data['username'], my_user.username)

我在Google上搜索了大约5个小时,以找出为什么appcontext_pushed.connected_不能发送任何内容...我得到的每个演示都无法正常工作。我已经用尽了勇气。

感谢tmt回答这样一个简单的问题。您向我展示它确实有效,我自己的代码中有问题。你给我勇气,这真的很重要。再次谢谢你!

答案 2 :(得分:0)

您不是唯一遇到这种奇怪行为的人!最近遇到这个问题,我觉得还不错,很多测试都可以从中受益。

从我所看到的来看,您的代码实际上并没有错(至少不是镜头下的部分)。我使用 app_context() 而不是 test_client()

也许有一些变化破坏了一些向后兼容性。肯定有点令人沮丧。线索在@contextmanager 装饰函数中:appcontext_pushed

但似乎 test_client() 没有推送 appcontext

这对我有用:

@contextmanager
def user_set(state, _user):
    def handler(sender, **kwargs):
        g.user = _user

    with appcontext_pushed.connected_to(handler, state):
        yield

with user_set(self.application, user):
    # use app_context() instead of test_client() context manager
    with app.app_context():
        client = app.test_client()
        # then make your request
        ...

这里的文档确实有点误导

编辑:不要没有勇气:)

相关问题