7

私はFlask-Testingを使用しています:

もう 1 つの落とし穴は、Flask-SQLAlchemy がすべてのリクエストの最後にセッション インスタンスも削除することです (scoped_session で SQLAlchemy を使用するスレッドセーフなアプリケーションと同様に)。したがって、client.get() または別のクライアント メソッドを呼び出すたびに、セッションに追加されたすべてのオブジェクトとともにセッションがクリアされます。

しかし、私はそれを見ていません。このテストは失敗します:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.testing import TestCase

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
@app.route('/')
def index():
  print 'before request:', `db.session`
  u = db.session.query(User).first()
  u.name = 'bob'
  return ''

class User(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String)

class SessionTest(TestCase):

  def create_app(self):
    return app

  def test_remove(self):
    db.drop_all()
    db.create_all()

    u = User()
    u.name = 'joe'
    db.session.add(u)
    db.session.commit()
    client = app.test_client()
    client.get('/')
    print 'after request:', `db.session`
    print u.name
    assert u not in db.session

(実行し$ nosetests test_file.pyて動作を確認してください。)

標準出力:

-------------------- >> begin captured stdout << ---------------------
before request: <sqlalchemy.orm.scoping.ScopedSession object at 0x10224c610>
after request: <sqlalchemy.orm.scoping.ScopedSession object at 0x10224c610>
bob

--------------------- >> end captured stdout << ----------------------

ドキュメントによると、ユーザーuは get リクエストの後にセッションに参加するべきではありませんが、そうです! なぜこれが起こっているのか誰にも分かりますか?

さらに、リクエストがコミットされていないにもかかわらず、 u.nameisbobおよび not ! joe(だから私はそれが同じセッションだと確信しています。)

記録のために、

$ pip freeze | grep Flask
Flask==0.10.1
Flask-Bcrypt==0.5.2
Flask-DebugToolbar==0.8.0
Flask-Failsafe==0.1
Flask-SQLAlchemy==0.16
Flask-Script==0.6.2
Flask-Testing==0.4
Flask-Uploads==0.1.3
Flask-WTF==0.8
4

3 に答える 3

10

混乱は、SQLAlchemy のセッションがスコープされているという事実から来ていると確信しています。つまり、各リクエストハンドラーが独自のセッションを作成および破棄します。

これが必要なのは、Web サーバーがマルチスレッド化できるため、複数の要求が同時に処理され、それぞれが異なるデータベース セッションで動作する可能性があるためです。

このため、リクエストのコンテキスト外で使用したセッションは、'/'ルートを処理するビュー関数が取得して最後に破棄するセッションとは異なる可能性があります。

更新:私は少し掘り下げて、このことを理解しました。

Flask-SQLAlchemy は にフックをインストールし、app.teardown_appcontextここで を呼び出しますdb.session.remove()

テスト環境は、アプリケーション コンテキストをプッシュ/ポップしないため、実際の要求の環境を完全には複製しません。そのため、リクエストの最後にセッションが削除されることはありません。

before_request補足として、 に登録された関数とafter_requestは、 を呼び出すときにも呼び出されないことに注意してくださいclient.get()

テストを少し変更するだけで、アプリケーション コンテキストのプッシュとポップを強制できます。

def test_remove(self):
  db.drop_all()
  db.create_all()

  u = User()
  u.name = 'joe'
  db.session.add(u)
  db.session.commit()
  with app.app_context():
      client = app.test_client()
      client.get('/')
  print 'after request:', `db.session`
  print u.name
  assert u not in db.session

この変更により、テストはパスします。

Flask-Testing のドキュメントは間違っているか、古くなっている可能性が高いようです。ある時点で説明どおりに機能した可能性がありますが、現在の Flask および Flask-SQLAlchemy バージョンでは正確ではありません。

これが役立つことを願っています!

于 2013-10-20T02:10:29.150 に答える
0

FlaskClientFlask-SQLAlchemyがFlaskバージョン 0.9以降shutdown_sessionでオンになっていると呼び出している間、リクエスト コンテキストで動作します。これが、テスト クライアントの呼び出しの後に何も起こらない理由です。これは、アプリ コンテキストがテストの前に開始され、後に閉じられるためです。app.teardown_appcontextflask.ext.testing.TestCasesetUptearDown

于 2013-10-18T08:52:59.973 に答える
0

Flask-Manage を使用してテストを実行しようとしたときに、同じ問題が発生しました。別のスレッドでテストを実行すると、問題が解決しました。

import threading
# some code omited
runner = unittest.TextTestRunner(verbosity=2)
t = threading.Thread(target=runner.run, args=[test_suite])
t.start()
t.join()
# other code omited
于 2016-01-06T07:12:54.423 に答える