この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
西田@大阪です。
今回はPythonの軽量WEBフレームワークであるFlaskと、ORMであるSQLAlchemyを使ってpytestのテストを書いてみたいと思います。
データベースを使った機能をテストするときに、テスト間の依存性をなくすためにテスト毎にデータベースのクリアが必要です。
Flaskの場合SQLiteを使った例が公開されているのですが、MySQLを使った例はありません。今回は Rails のテストなどと同じように、テスト毎にロールバックさせる方式で実装したいと思います。
今回試した環境はこちら
- python: 3.6
- Flask: 1.0.2
- Flask-SQLAlchemy: 2.3.2
- SQLAlchemy: 1.2.9
- pytest: 3.6.2
コミット時の挙動を変更する
commit()
メソッドの挙動を変えるためにFlask-SQLAlchemyの SignallingSession
を継承したクラスを作成します。
from flask_sqlalchemy import SignallingSession
class TestSignallingSession(SignallingSession):
def commit(self):
self.flush() # セッションが保持しているデータをすべてデータベースに書き込む
self.expire_all() # セッションが保持してるデータをクリアしデータベースより読むこむようにする
上記で作成したクラスを利用するため、SQLAlchemy
を継承したクラスを作成し、create_session
メソッドをオーバーライドします。
from flask_sqlalchemy import SQLAlchemy, orm
class TestSQLAlchemy(SQLAlchemy):
def create_session(self, options):
return orm.sessionmaker(class_=TestSignallingSession, db=self, **options)
テスト環境時にコミット時の挙動を変更したセッションが使えるように切り替えるようにします。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
if app.testing:
db = TestSQLAlchemy(app)
else:
db = SQLAlchemy(app)
# 以降はこの "db" を使って操作データベースを操作します
テスト終了後にロールバックする
conftest.py
にテスト毎にロールバックするように後処理を追加します。
import app # flaskのアプリケーションをimport
import pytest
# 毎テスト毎に rollback() メソッドを実行する
@pytest.fixture(scope='function', autouse=True)
def scope_function():
yield
app.db.session.rollback()
さいごに
Flaskは機能が足りてないながらも軽量で、決まった構成がないぶん自由に組めるのが魅力に感じました。
本格的なアプリケーションをつくるのには苦労をしそうですが、少、中規模のアプリケーションを作るにはちょうどよさそうです。