使用TransactionTestCase和pytest进行测试失败

时间:2016-06-30 13:53:44

标签: python django pytest django-1.8

我的单元测试和django管理事务的方式存在问题。

在我的代码中,我有一个函数:

def send():
    autocommit = transaction.set_autocommit(False)
    try:
         # stuff
    finally:
         transaction.rollback()
         transaction.set_autocommit(autocommit)

在我的测试中,我有:

class MyTest(TransactionTestCase):
    def test_send(self):
        send()

我遇到的问题是我的test_send成功通过,但不是80%的其他测试。

其他测试的交易似乎失败了

btw我正在使用py.test来运行我的测试

编辑: 当我仅使用测试运行时更清楚 myapp.test.test_module.py运行正常并且所有3个测试都通过但是当我运行所有测试时大部分失败,将尝试生成测试应用程序

我的所有测试都通过了django的默认测试运行器

EDIT2: 以下是测试此问题的最小示例:

class ManagementTestCase(TransactionTestCase):

    def test_transfer_ubl(self, MockExact):
        pass

class TestTestCase(TestCase):

     def test_1_user(self):
         get_user_model().objects.get(username="admin")
         self.assertEqual(get_user_model().objects.all().count(), 1)

请记住,有一个数据迁移可以添加一个" admin" user(TestTestCase单独成功,但之前没有运行ManagmentTestCase)

似乎autocommit与它无关。

3 个答案:

答案 0 :(得分:4)

TestCase类将测试包装在两个原子块中。因此,如果您继承自transaction.set_autocommit(),则无法使用transaction.rollback()TestCase

正如文档所说,如果您正在测试特定的数据库事务行为,则应使用TransactionTestCase

答案 1 :(得分:4)

autocommit = transaction.set_autocommit(False)函数中{{}}感觉错误。在这里完成禁用事务可能是出于测试目的,但经验法则是将测试逻辑保留在代码之外。

正如@Alasdair指出的那样,django docs表示“出于性能原因,Django的TestCase类还会在事务中包装每个测试。”

从您的问题中不清楚您是否正在测试特定的数据库事务逻辑,如果是这种情况,那么@ Alasdair使用send的答案是可行的。

否则,从TransactionTestCase函数中的stuff附近删除事务上下文切换应该会有所帮助。

由于您提到send作为您的测试运行员,我还建议您使用pytest。 Pytest-django插件具有很好的功能,例如使用markers选择性地设置一些测试以进行交易。

pytest

如果安装插件太多,那么您可以滚动自己的pytest.mark.django_db(transaction=False) 夹具。像

transaction manage

您的 @pytest.fixture def no_transaction(request): autocommit = transaction.set_autocommit(False) def rollback(): transaction.rollback() transaction.set_autocommit(True) request.addfinalizer(rollback) 将需要test_send灯具。

no_transaction

答案 2 :(得分:1)

对于那些仍在寻找解决方案的人,serialized_rollback选项是一种解决方法:

class ManagementTestCase(TransactionTestCase):
    serialized_rollback = True

    def test_transfer_ubl(self, MockExact):
        pass

class TestTestCase(TestCase):

     def test_1_user(self):
         get_user_model().objects.get(username="admin")
         self.assertEqual(get_user_model().objects.all().count(), 1)

来自docs

Django可以通过在serialized_rollbackTrue的正文中将TestCase选项设置为TransactionTestCase来为每个测试案例重新加载数据,但是请注意这样会使测试套件的速度降低大约3倍。

很遗憾,pytest-django仍缺少此功能。