我的代码是错误的。所以问题结束了。非常感谢tmt。
===================起源问题⬇️================
我尝试按照flask-doc测试我的烧瓶应用程序(python3.8.1)
我想在测试前请求为我的应用设置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
答案 0 :(得分:0)
您已经从文档中覆盖了示例的某些部分,因此在调用g.name
时users_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
...
这里的文档确实有点误导
编辑:不要没有勇气:)