Using multiple test_clients in parallel to simulate multi-user workflows
还没有人认领这个 Issue。
评估
调研方向
先从 issue 中的 client_a、client_b fixture 和 test_multi_clients_min 示例开始,然后查看 pytest-flask 的现有 test-client fixture 行为。当不同的客户端能够在一次测试中维护不同的已登录用户,并且请求之间无需登录或注销,同时通过一个测试覆盖多客户端工作流时,该功能就完成了。
由索引模型根据 Issue 内容生成。
描述
Is it possible to test with multiple clients, each logged in as different user to integration-test workflows, that involve multiple users?
I'm currently using flask-security and pytest for testing and wanted to give pytest-flask a try.
For example:
def test_message(client_a, client_b):
r = client_a.post("/api/msg_send", data={"recipient": "user_b", "text": "Hello!"})
assert r.status_code == 200
r = client_b.get("/api/msg_list")
assert r.status_code == 200
assert len(r.json['messages']) > 0
msg_id = r.json['messages'][0]['id']
r = client_b.get("/api/msg_get", data={"id": msg_id})
assert r.status_code == 200
assert r.json['text'] == "Hello!"
r = client_a.get("/api/msg_get", data={"id": msg_id})
assert r.status_code == 200
assert r.json['seen'] == True
...
With pytest, I'm preparing the test_clients in a fixture, like in this minimal working example:
import os
from flask import Flask, render_template_string
import flask_mail
from flask_security import Security, current_user, auth_required, hash_password, \
SQLAlchemySessionUserDatastore, UserMixin, RoleMixin
from flask.json.tag import TaggedJSONSerializer
from itsdangerous import URLSafeTimedSerializer
import pytest
from sqlalchemy import create_engine, Boolean, DateTime, Column, Integer, \
String, ForeignKey, UnicodeText
from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///:memory:')
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
class RolesUsers(Base):
__tablename__ = 'roles_users'
id = Column(Integer(), primary_key=True)
user_id = Column('user_id', Integer(), ForeignKey('user.id'))
role_id = Column('role_id', Integer(), ForeignKey('role.id'))
class Role(Base, RoleMixin):
__tablename__ = 'role'
id = Column(Integer(), primary_key=True)
name = Column(String(80), unique=True)
description = Column(String(255))
permissions = Column(UnicodeText)
class User(Base, UserMixin):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
email = Column(String(255), unique=True)
username = Column(String(255), unique=True, nullable=True)
password = Column(String(255), nullable=False)
last_login_at = Column(DateTime())
current_login_at = Column(DateTime())
last_login_ip = Column(String(100))
current_login_ip = Column(String(100))
login_count = Column(Integer)
active = Column(Boolean())
fs_uniquifier = Column(String(255), unique=True, nullable=False)
confirmed_at = Column(DateTime())
roles = relationship('Role', secondary='roles_users',
backref=backref('users', lazy='dynamic'))
def init_db():
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
Base.metadata.create_all(bind=engine)
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['TESTING'] = True
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
app.config['WTF_CSRF_ENABLED'] = False
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_EMAIL_VALIDATOR_ARGS'] = {"check_deliverability": False}
app.config['SECURITY_PASSWORD_HASH'] = "plaintext"
# Setup Flask-Security
user_datastore = SQLAlchemySessionUserDatastore(db_session, User, Role)
app.security = Security(app, user_datastore)
# flask-mail extension
mail = flask_mail.Mail()
# initialize mail extension
mail.init_app(app)
# Views
@app.route("/")
@auth_required()
def home():
return render_template_string('Hello {{email}} !', email=current_user.email)
# one time setup
with app.app_context():
# Create a user to test with
init_db()
if __name__ == '__main__':
# run application (can also use flask run)
app.run()
@pytest.fixture()
def application():
with app.app_context():
if not app.security.datastore.find_user(email="[email protected]"):
app.security.datastore.create_user(email="[email protected]", password=hash_password("password"))
db_session.commit()
yield app
@pytest.fixture()
def client_a(application):
client_a = application.test_client()
response = client_a.post("/register", data=dict(
email="[email protected]",
password="client A password",
password_confirm="client A password"),
follow_redirects=False
)
yield client_a
@pytest.fixture()
def client_b(application):
client_b = application.test_client()
response_b = client_b.post(
"/register", data=dict(
email="[email protected]",
password="client B password",
password_confirm="client B password"
),
follow_redirects=False
)
yield client_b
def get_existing_session(client):
cookie = next(
(cookie for cookie in client.cookie_jar if cookie.name == "session"), None
)
if cookie:
serializer = URLSafeTimedSerializer("secret", serializer=TaggedJSONSerializer())
val = serializer.loads_unsafe(cookie.value)
return val[1]
def test_multi_clients_min(application, client_a, client_b):
client_a_session = get_existing_session(client_a)
client_b_session = get_existing_session(client_b)
assert client_a_session["_user_id"] != client_b_session["_user_id"]
It would be awesome if this would be possible with pytest-flask without logging in/out after every request that comes from a differnt user, as it would probably be much cleaner than with pytest alone.
- 主要语言
- Python
- 星标
- 499
- 派生
- 93
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
pytest-dev/pytest-flask 的其他 Issue
-
bug
难度 3/5 1-2 天 新手友好度 35/100
pytest-dev/pytest-flask#190 · 1 条评论 · 1 个 reaction ·
-
bug
难度 4/5 3-5 天 新手友好度 35/100
pytest-dev/pytest-flask#187 · 7 条评论 ·
-
bug
难度 2/5 1-3 小时 新手友好度 45/100
pytest-dev/pytest-flask#175 · 3 条评论 ·
-
feature request
难度 4/5 3-5 天 新手友好度 45/100
pytest-dev/pytest-flask#153 · 1 条评论 ·
-
stale
难度 5/5 一周以上 新手友好度 20/100
pytest-dev/pytest-flask#103 ·
查看 pytest-dev/pytest-flask 的全部 Issue
相似的 Issue
-
[Bug] @deck.gl/arcgis dist import resolves to unpublished @deck.gl/core source path (9.3.11, 9.4.0)未关闭
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
workflow: a tick's dispatch counts as 'only this step', and no review self-grants a round unattended未关闭workflow
难度 2/5 1-3 小时 新手友好度 85/100
kristofdegrave/homeassistant-smart-charging#1505 ·
维护者通常 1 天内回复
-
metadata submission
难度 2/5 1-3 小时 新手友好度 82/100
-
bug
难度 2/5 1-3 小时 新手友好度 65/100
canonical/content-cache-operator#163 · 1 条评论 ·
维护者通常 1 天内回复
-
[submission]未关闭submission
难度 1/5 1 小时以内 新手友好度 65/100
leanprover/lean-eval-submissions#1852 ·
维护者通常 1 天内回复