使用Flask,Flask-SQLAlchemy和SQLAlchemy进行单元测试时,如何在线程之间隔离测试数据库?

时间:2017-07-10 20:40:18

标签: python flask flask-sqlalchemy

假设我正在关注Flask-SQLAlchemy快速启动示例,并且我想添加几个单元测试。

我的模型可能类似于:

db = SQLAlchemy(app)
Base = db.Model

class Account(Base):
    id = Column(Integer, primary_key=True)
    name = Column(String(1000))

对于单元测试,我想要为每个测试创建和销毁数据库。

def _setup_database():
    db_name = 'test_%s' % random.randint(0,999999)

    # (Code that creates database with db_name and setups the schema)

    app.config['DB_NAME'] = db_name
    app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///{}'.format(db_name)


def _destroy_db():
    db_name = app.config['DB_NAME']

    # Code that destroys the test db


class TestAccounts(unittest.TestCase):
    def setUp(self):
        _setup_database()

    def tearDown(self):
        _destroy_db()

    def test_new_signup(self):
        account = models.Account(...)

    def test_something_else(self):
        account = models.Account(...)

这里的问题是,如果我在单元测试是多线程的环境中运行此代码,则存在竞争条件。通常同时设置两个数据库,并将app.config指向其中一个。

如果我直接使用SQLAlchemy,我会为每个测试创建一个新会话并使用该会话。但Flask-SQLAlchemy为我创建了会话,因此,似乎依赖于有一个全局app.config['SQLALCHEMY_DATABASE_URI']指向数据库。

使用Flask-SQLAlchemy创建测试数据库并将测试线程指向它们的正确方法是什么?

1 个答案:

答案 0 :(得分:3)

对于每个TestCase.setUp函数运行unittest TestCase.tearDowntest_

因此,在多线程进程中运行测试确实会创建一个RC。您需要在单个线程中运行测试。这将修复RC,但您仍然会为每个测试创建并销毁所有数据库,这是不必要的和缓慢的。

更好的解决方案是使用setUpclass and tearDownClass方法,每个测试类只运行一次。

如果您需要为模块中的所有类运行此灯具,还有setUpModuletearDownModule

如果你现在需要为整个会话设置灯具你有问题...恕我直言,是时候切换到pytest。