测试瓶sql炼金术

Ell*_*bal 23 python testing session sqlalchemy flask

我在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) 
Run Code Online (Sandbox Code Playgroud)

cod*_*eek 20

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

使用SQLAlchemy进行测试

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

首先,确保将数据库URI设置为生产数据库以外的其他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()
Run Code Online (Sandbox Code Playgroud)

  • 鉴于这个问题的赞成,我一定会错过一些东西,但这里的'create_app`的实现不是很重要吗?链接的文档没有解释`myapp.create_app`应该做什么(例如,与db相关). (16认同)
  • create_app我认为应该像你的app.py一样返回应用程序 - 例如app = Flask(__ name__).因为我不想要一个不同的配置用于测试和生产,我只是从我的主命名空间导入应用程序,然后在create_app中返回它,它工作正常. (2认同)
  • @ForeverWintr 这是一个 wsgi 应用程序工厂,它是一种常见的烧瓶模式,可以简化测试和中间件实现。它可以表示为:`create_app = lambda:flask.Flask(__name__)` (2认同)

Ale*_*sen 14

这是我最近运行单元测试的方式.我假设您正在使用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()
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么在tearDown()中再次使用self.app = Flask(__ name __)` 在setUp()中设置后我不认为你需要再次这样做. (3认同)

小智 9

关于codegeek使用Flask-Testing的答案,我很难理解是什么createapp().Flask-SqlAlchemy Contextxts简介为我提供了一些关于如何动态地将SQLAlchemy对象绑定到应用程序的指针.在这种情况下,绑定到测试应用程序.

基本上:

  1. 创建一个烧瓶sqlalchemy对象,但不要传入app对象
  2. 在create_app函数中,创建测试应用程序,并动态绑定SQLAlchemy.

你的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
Run Code Online (Sandbox Code Playgroud)

然后,您可以按照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()
Run Code Online (Sandbox Code Playgroud)

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