如何正确运行查询Flask-SQLAlchemy数据库的连续测试?

时间:2019-05-06 09:03:13

标签: python unit-testing sqlalchemy flask-sqlalchemy flask-testing

我正在使用SQLAlchemy作为ORM为Flask项目设置单元测试。对于我的测试,每次运行单个单元测试时,我需要设置一个新的测试数据库。无论如何,即使我独立运行这些测试也能成功,但似乎无法运行查询数据库的连续测试。

我使用pdfjs-dist软件包,并遵循他们的文档here

这是一个说明问题的有效示例:

pdf2png.js

flask-testing

app.py:

from flask import Flask


def create_app():
    app = Flask(__name__)
    return app


if __name__ == '__main__':
    app = create_app()
    app.run(port=8080)

database.py:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

models.py:

from database import db


class TestModel(db.Model):
    """Model for testing."""

    __tablename__ = 'test_models'
    id = db.Column(db.Integer,
                   primary_key=True
                   )

test/__init__.py:

from flask_testing import TestCase

from app import create_app
from database import db


class BaseTestCase(TestCase):
    def create_app(self):
        app = create_app()
        app.config.update({
            'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
            'SQLALCHEMY_TRACK_MODIFICATIONS': False,
            'TESTING': True
        })
        db.init_app(app)
        return app

    def setUp(self):
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

因此,如果单独运行,我可以正常运行test/test_app.py:from models import TestModel from test import BaseTestCase from database import db test_model = TestModel() class TestApp(BaseTestCase): """WebpageEnricherController integration test stubs""" def _add_to_db(self, record): db.session.add(record) db.session.commit() self.assertTrue(record in db.session) def test_first(self): """ This test runs perfectly fine """ self._add_to_db(test_model) result = db.session.query(TestModel).first() self.assertIsNotNone(result, 'Nothing in the database') def test_second(self): """ This test runs fine in isolation, but fails if run consecutively after the first test """ self._add_to_db(test_model) result = db.session.query(TestModel).first() self.assertIsNotNone(result, 'Nothing in the database') if __name__ == '__main__': import unittest unittest.main() 。如果我连续运行它们,则第一个测试通过,但第二个测试失败并显示:

TestApp.test_first

在数据库设置和拆卸中出了点问题,但是我不知道是什么。如何正确设置?

1 个答案:

答案 0 :(得分:1)

答案是,通过重用一次在模块范围(TestModel)中定义的单个test_model = TestModel()实例,您正在一次测试和另一次测试之间泄漏状态。

第一次测试开始时该实例的状态为transient

  

不在会话中并且没有保存到数据库的实例;   即它没有数据库身份。这样的对象唯一的关系   对于ORM而言,它的类具有与之关联的mapper()。

第二次测试开始时对象的状态为detached

  

已分离-对应或先前对应的实例   到数据库中的记录,但当前不在任何会话中。的   分离的对象将包含数据库标识标记,但是   由于它与会话无关,因此未知是否   该数据库标识实际上不在目标数据库中。   分离的对象可以安全使用,除非它们没有   能够加载卸载的属性或以前的属性   标记为“过期”。

测试之间的这种相互依赖性几乎总是一个坏主意。您可以在每次测试结束时在对象上使用make_transient()

class BaseTestCase(TestCase):
    ...
    def tearDown(self):
        db.session.remove()
        db.drop_all()
        make_transient(test_model)

或者您应该为每个测试构建一个新的TestModel实例:

class BaseTestCase(TestCase):
    ...
    def setUp(self):
        db.create_all()
        self.test_model = TestModel()


class TestApp(BaseTestCase):
    ...
    def test_xxxxx(self):
        self._add_to_db(self.test_model)

我认为后者是更好的选择,因为在测试之间不存在任何其他泄漏状态的危险。