如何确保使用nosetests调用tearDown(在测试中未捕获的异常情况下)?

时间:2013-12-27 17:03:06

标签: python unit-testing sqlalchemy

我有一个同时使用setUptearDown方法的测试用例:

TESTCONF = SafeConfigParser(...)
ENGINE = create_engine(TESTCONF.get('database', 'dsn'))

class TestBase(TestCase):

    def setUp(self):
        self.config = TESTCONF
        self.connection = ENGINE.connect()
        self.trans = self.connection.begin()
        self.session = Session(bind=self.connection)

    def tearDown(self):
        print "post teardown 0", ENGINE.pool.status()
        self.trans.rollback()
        print "post teardown 1", ENGINE.pool.status()
        self.session.close()
        print "post teardown 2", ENGINE.pool.status()
        self.connection.close()
        print "post teardown 3", ENGINE.pool.status(), "\n"

所有与DB相关的测试用例都继承自此类。似乎并不总是调用tearDown。我无法本地化错误。有一次,测试运行器挂起

self.connection = ENGINE.connect()

我假设并不总是调用close方法来释放池中的连接。

任何想法要找什么?

更新:我是如何添加一些打印语句(也在上面的示例代码中添加它们),而我的初始文件是正确的。有些连接没有正确关闭,也没有交回池中。对于测试中的所有“错误”(不是“失败”),都会发生这种情况。以下块显示了使用上述tearDown方法获得的输出(为简洁起见,过早切断)。如您所见,有错误的行(以Epre而非.pre开头的行)不会调用任何tearDown行。甚至没有显示post tearDown 0消息!

我现在已经将错误追溯到使用self.assertRaisesRegEx而不是self.assertRaisesRegExp,因此异常会在 unit-test 中引发,而不是在测试代码内部!< / p>

pre setup Pool size: 5  Connections in pool: 0 Current Overflow: -5 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
'TestCommonBase' object has no attribute 'assertRaisesRegex'
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
'TestCommonBase' object has no attribute 'assertRaisesRegex'
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0 

.SSpre setup Pool size: 5  Connections in pool: 1 Current Overflow: -4 Current Checked out connections: 0
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
ESpre setup Pool size: 5  Connections in pool: 0 Current Overflow: -4 Current Checked out connections: 1
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -3 Current Checked out connections: 2
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: -3 Current Checked out connections: 2
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -2 Current Checked out connections: 3
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: -2 Current Checked out connections: 3
post connect Pool size: 5  Connections in pool: 0 Current Overflow: -1 Current Checked out connections: 4
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: -1 Current Checked out connections: 4
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 0 Current Checked out connections: 5
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 0 Current Checked out connections: 5
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 1 Current Checked out connections: 6
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 1 Current Checked out connections: 6
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 2 Current Checked out connections: 7
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 2 Current Checked out connections: 7
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 3 Current Checked out connections: 8
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 3 Current Checked out connections: 8
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 4 Current Checked out connections: 9
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 4 Current Checked out connections: 9
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 5 Current Checked out connections: 10
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 5 Current Checked out connections: 10
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 6 Current Checked out connections: 11
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 6 Current Checked out connections: 11
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 7 Current Checked out connections: 12
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 7 Current Checked out connections: 12
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 8 Current Checked out connections: 13
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 8 Current Checked out connections: 13
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 9 Current Checked out connections: 14
Epre setup Pool size: 5  Connections in pool: 0 Current Overflow: 9 Current Checked out connections: 14
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14 

.pre setup Pool size: 5  Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14
post connect Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
Epost teardown 0 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 1 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 2 Pool size: 5  Connections in pool: 0 Current Overflow: 10 Current Checked out connections: 15
post teardown 3 Pool size: 5  Connections in pool: 1 Current Overflow: 10 Current Checked out connections: 14 

其他详细信息:

为了这个问题,我写了一个非常小的可重复的例子。不幸的是,没有人表现出这种行为:

class MyTest(TestCase):
    """
    Example test case meant to demonstrate that ``tearDown`` is not called.

    It turns out, in this case, ``tearDown`` *is* called as expected!
    """

    def setUp(self):
        print "setup"

    def tearDown(self):
        print "tearDown"

    def test_failing(self):
        print int('yes')


if __name__ == '__main__':
    main()

那么是什么让这个例子与我的真实世界代码不同?为什么在简单示例中调用tearDown,而不是在我的生产代码中调用?

我会继续调查......

1 个答案:

答案 0 :(得分:1)

如果您怀疑未调用tearDown,那么我可以建议您在测试中使用上下文管理器进行资源分配。 with语句保证如果__enter__()方法返回时没有错误,则始终会调用__exit__()

以下是修改了使用上下文管理器进行连接分配的示例:

TESTCONF = SafeConfigParser(...)
ENGINE = create_engine(TESTCONF.get('database', 'dsn'))

class DBConnection(object):
    def __init__(self, engine):
        self.engine = engine

    def __enter__(self):
        self.connection = engine.connect()
        self.trans = self.connection.begin()
        self.session = Session(bind=self.connection)
        # return value can be accessed using `as` directive
        return self.connection, self.trans, self.session

    def __exit__(self, exc_type, exc_val, traceback):
        self.trans.rollback()
        self.session.close()
        self.connection.close()


class TestBase(unittest.TestCase):

    def setUp(self):
        self.config = TESTCONF

    def run(self, result=None):
        with DBConnection(ENGINE) as db_conn:
            self.connection, self.trans, self.session = db_conn
            super(MyTest, self).run(result)

如果DBConnection类看起来太笨重,你也可以使用contextlib:

from contextlib import contextmanager

@contextmanager
def DBConnection(engine):
    connection = engine.connect()
    trans = connection.begin()
    session = Session(bind=connection)
    yield connection, trans, session
    trans.close()
    session.close()
    connection.close()