回滚Flask中的测试之间的许多事务

时间:2014-10-06 20:48:28

标签: flask sqlalchemy flask-sqlalchemy

我的测试需要很长时间才能运行,我试图在测试之间回滚事务,而不是在测试之间删除和创建表。

问题是在某些测试中我做了多次提交。

编辑:如何在测试之间回滚事务,以便测试运行得更快

这是用于测试的Base类。

import unittest
from app import create_app
from app.core import db
from test_client import TestClient, TestResponse


class TestBase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.app.response_class = TestResponse
        self.app.test_client_class = TestClient
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        db.get_engine(self.app).dispose()
        self.app_context.pop()

这是我尝试回滚交易。

class TestBase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.app = create_app('testing')
        cls.app_context = cls.app.app_context()
        cls.app_context.push()
        cls.app.response_class = TestResponse
        cls.app.test_client_class = TestClient

        db.create_all()

    @classmethod
    def tearDown(cls):
        db.session.remove()
        db.drop_all()
        db.get_engine(cls.app).dispose()

    def setUp(self):
        self.app_content = self.app.app_context()
        self.app_content.push()
        db.session.begin(subtransactions=True)

    def tearDown(self):
        db.session.rollback()
        db.session.close()

        self.app_context.pop()

4 个答案:

答案 0 :(得分:4)

这是我们用来执行此操作的代码。确保在您的设置中调用__start_transaction,并在您的拆解中调用__close_transaction(如果您使用的是flask-sqlalchemy,则使用应用程序上下文)。作为进一步的提示,仅在命中数据库的测试用例中继承此代码,并从检查业务逻辑的代码中分离检查数据库函数的代码,因为这些代码仍将以更快的速度运行。

def __start_transaction(self):
    # Create a db session outside of the ORM that we can roll back
    self.connection = db.engine.connect()
    self.trans = self.connection.begin()

    # bind db.session to that connection, and start a nested transaction
    db.session = db.create_scoped_session(options={'bind': self.connection})
    db.session.begin_nested()

    # sets a listener on db.session so that whenever the transaction ends-
    # commit() or rollback() - it restarts the nested transaction
    @event.listens_for(db.session, "after_transaction_end")
    def restart_savepoint(session, transaction):
        if transaction.nested and not transaction._parent.nested:
            session.begin_nested()

    self.__after_transaction_end_listener = restart_savepoint

def __close_transaction(self):
    # Remove listener
    event.remove(db.session, "after_transaction_end", self.__after_transaction_end_listener)

    # Roll back the open transaction and return the db connection to
    # the pool
    db.session.close()

    # The app was holding the db connection even after the session was closed.
    # This caused the db to run out of connections before the tests finished.
    # Disposing of the engine from each created app handles this.
    db.get_engine(self.app).dispose()

    self.trans.rollback()
    self.connection.invalidate()

答案 1 :(得分:3)

您可以使用Session.begin_nested。只要您的所有测试都正确调用commit来关闭他们的子事务,我认为您可以简单地执行

session.begin_nested()
run_test(session)
session.rollback()

在我看来,它似乎应该更快。但是,可能在某种程度上取决于您的数据库。

答案 2 :(得分:0)

虽然这个答案没有在技术上回答你的问题,但你确实提到回滚测试背后的原因是因为它们需要很长时间才能运行,所以我不知道喜欢提供替代解决方案:

在开始运行测试套件时创建表,并在完成所有测试后删除它们。然后简单地public void doProcessing(View view) { // READING THE RGBA MAT Mat rgbaMat4Mask = Highgui.imread("/mnt/sdcard/DCIM/rgbaMat4Mask.bmp"); // CONVERTING TO HSV Mat hsvMat4Mask = new Mat(); Imgproc.cvtColor(rgbaMat4Mask, hsvMat4Mask, Imgproc.COLOR_BGR2HSV); Highgui.imwrite("/mnt/sdcard/DCIM/hsvMat4Mask.bmp", hsvMat4Mask);//check // CREATING A FILTER/MASK FOR RED COLORED BLOB Mat maskedMat = new Mat(); Core.inRange(hsvMat4Mask, new Scalar(0, 100, 100), new Scalar(10, 255, 255), maskedMat); Highgui.imwrite("/mnt/sdcard/DCIM/maskedMat.bmp", maskedMat);// check // COPYING THE MASK TO AN EMPTY MAT // STEP 1: List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); Imgproc.findContours(maskedMat, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE); //STEP 2: Mat newMat4Mask = new Mat(rgbaMat4Mask.rows(), rgbaMat4Mask.cols(), CvType.CV_8UC1); newMat4Mask.setTo(new Scalar(0)); Imgproc.drawContours(newMat4Mask, contours, 0, new Scalar(255), -1);//TODO Using -1 instead of CV_FILLED. Highgui.imwrite("/mnt/sdcard/DCIM/newMatWithMask.bmp", newMat4Mask);// check //STEP 3 Log.i(TAG, "HAPPY rows:"+contours.get(0).rows()+" columns:"+contours.get(0).cols()); Mat newMatwithMaskFinished = new Mat(contours.get(0).rows(), contours.get(0).cols(), CvType.CV_8UC3); newMatwithMaskFinished.setTo(new Scalar(120, 255, 255)); rgbaMat4Mask.copyTo(newMatwithMaskFinished, newMat4Mask); Highgui.imwrite("/mnt/sdcard/DCIM/newMatwithMaskFinished.bmp", newMatwithMaskFinished);//check*/ } 完成每个测试tearDown,而不是完全丢弃它们。

我花了很长时间试图找出如何通过原始海报提出的回滚加速我的测试,并发现它非常混乱,因为它涉及嵌套事务。但是,一旦我尝试了上述方法,我的测试套件的运行速度大约是我的两倍,这对我来说已经足够了。

答案 3 :(得分:0)

如果您正在使用pytest,则可以创建以下灯具:

@pytest.fixture(scope='session')
def app():
    app = create_app('config.TestingConfig')
    log.info('Initializing Application context.')

    ctx = app.app_context()
    ctx.push()

    yield app
    log.info('Destroying Application context.')
    ctx.pop()

@pytest.fixture(scope='session')
def db():
    log.info('Initializating the database')

    _db.drop_all()
    _db.create_all()

    session = _db.session
    seed_data_if_not_exists(session)
    session.commit()

    yield _db

    log.info('Destroying the database')
    session.rollback()
    #_db.drop_all() #if necessary

@pytest.fixture(scope='function')
def session(app, db):
    log.info("Creating database session")

    session = db.session
    session.begin_nested()

    yield session

    log.info("Rolling back database session")
    session.rollback()
相关问题