测试瓶sql炼金术

时间:2013-07-22 15:29:53

标签: python session testing flask sqlalchemy

我在Flask上有一个工作的Web应用程序,带有SqlAlchemy用于审核新闻,它有一些api方法来处理审核请求,例如批准,拒绝当前选择的新闻,列出它们等等。我想为此编写单元测试方法,我让它们工作,但我不明白,如何在一个数据库会话中实现从测试用例中执行的所有请求,以便我可以删除对数据库的所有更改。或者是否有其他更清洁或正确的方法来做到这一点? 我发现可能我需要的只是SqlAlchemy中的“scoped_session”,但实现它的所有工作都失败了。如果这是正确的方法,请告诉我在哪里使用这行代码(在设置中,或在测试用例set_up方法中)。

from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
session_factory = sessionmaker()
Session = scoped_session(session_factory) 

3 个答案:

答案 0 :(得分:18)

我建议你使用Flask-Testing扩展名。这是经过批准的扩展程序,可让您根据需要进行单元测试。它还有一个针对SQLAlchemy的特定部分。

使用SQLAlchemy进行测试

如果您使用Flask-Testing和SQLAlchemy,这将涵盖几个要点。假设您正在使用Flask-SQLAlchemy扩展,但如果不是,则示例不应该太难以适应您自己的特定设置。

首先,确保将数据库URI设置为生产数据库以外的其他内容!其次,每次测试运行时创建和删除表通常都是个好主意,以确保干净的测试:“

from flask.ext.testing import TestCase

from myapp import create_app, db

class MyTest(TestCase):

    SQLALCHEMY_DATABASE_URI = "sqlite://"
    TESTING = True

    def create_app(self):

        # pass in test configuration
        return create_app(self)

    def setUp(self):

        db.create_all()

    def tearDown(self):

        db.session.remove()
        db.drop_all()

答案 1 :(得分:11)

这是我最近运行单元测试的方式。我假设您正在使用SQLAlchemy,因为您正在使用模型类。我还假设你的所有表都被定义为SQLAlchemy模型类。

from flask import Flask
import unittest

from app import db
from app.models import Log
from constants import test_logs

class appDBTests(unittest.TestCase):

    def setUp(self):
        """
        Creates a new database for the unit test to use
        """
        self.app = Flask(__name__)
        db.init_app(self.app)
        with self.app.app_context():
            db.create_all()
            self.populate_db() # Your function that adds test data.

    def tearDown(self):
        """
        Ensures that the database is emptied for next unit test
        """
        self.app = Flask(__name__)
        db.init_app(self.app)
        with self.app.app_context():
            db.drop_all()

由于您使用的是与应用程序相同的数据库设置,因此您可以在运行的每个单元测试中构建和销毁测试数据库。

答案 2 :(得分:6)

关于使用Flask-Testing的codegeek答案,我无法理解createapp()的作用。 Flask-SqlAlchemy Introduction into Contexts为我提供了一些关于如何动态地将SQLAlchemy对象绑定到应用程序的指针。在这种情况下,绑定到测试应用程序。

基本上:

  1. 创建一个烧瓶sqlalchemy对象,但不传递应用程序对象
  2. 在create_app函数中,创建测试应用程序,并动态绑定SQLAlchemy。
  3. 您的 myapp.py

    # don't pass in the app object yet
    db = SQLAlchemy()
    
    def create_test_app():
        app = Flask(__name__)
        app.config['TESTING'] = True
        app.config["SQLALCHEMY_DATABASE_URI"] = "xxxxxxtestdatabasexxx"
        # Dynamically bind SQLAlchemy to application
        db.init_app(app)
        app.app_context().push() # this does the binding
        return app
    
    # you can create another app context here, say for production
    def create_production_app():
        app = Flask(__name__)
        app.config["SQLALCHEMY_DATABASE_URI"] = "xxxxxxproductionxxxx"
        # Dynamically bind SQLAlchemy to application
        db.init_app(app)
        app.app_context().push()
        return app
    

    然后您可以按照Flask-Test Documentation

    中所述的codegeek解决方案进行操作
    from flask.ext.testing import TestCase
    from myapp import create_app, db
    
    class MyTest(TestCase):
    
        # I removed some config passing here
        def create_app(self):
            return create_test_app()
    
        def setUp(self):
    
            db.create_all()
    
        def tearDown(self):
    
            db.session.remove()
            db.drop_all()
    

    此解决方案的优点在于您可以使用函数创建不同的应用程序并动态绑定SQLAlchemy对象。每个应用程序可以用于不同的目例如,一个用于生产,一个用于单元测试。在生产的情况下,您可以在顶级烧瓶应用程序中调用create_production_application()。