Django 测试 - 内部错误:当前事务被中止,命令被忽略直到事务块结束

发布于 2024-09-16 17:00:26 字数 1601 浏览 1 评论 0原文

在我的测试中,我不仅测试完美的情况,而且特别测试边缘情况和错误条件。所以我想确保一些唯一性约束起作用。

虽然我的测试和测试装置非常复杂,但我能够将问题追溯到以下示例,该示例不使用任何自定义模型。要重现该行为,只需将代码保存到tests.py 中并运行django 测试运行程序。

from django.contrib.auth.models import User
from django.db import IntegrityError
from django.test import TransactionTestCase

class TransProblemTest(TransactionTestCase):
    def test_uniqueness1(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

    def test_uniqueness2(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

具有单个测试方法的测试类可以工作,但如果有两个相同的方法实现,则会失败。 第一个测试抛出异常破坏了 Django 测试环境,并使以下所有测试失败。

我正在使用 Django 1.1 和 Ubuntu 10.04、Postgres 8.4 和 psycopg2。

Django 1.2 中还存在这个问题吗?

这是一个已知的错误还是我遗漏了什么?

In my tests I do not only test for the perfect case, but especially for edge cases and error conditions. So I wanted to ensure some uniqueness constraints work.

While my test and test fixtures are pretty complicated I was able to track the problem down to the following example, which does not use any custom models. To reproduce the behaviour just save the code into tests.py and run the django test runner.

from django.contrib.auth.models import User
from django.db import IntegrityError
from django.test import TransactionTestCase

class TransProblemTest(TransactionTestCase):
    def test_uniqueness1(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

    def test_uniqueness2(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

A test class with a single test method works, but fails with two identical method implementations.
The first test throwing exception breaks the Django testing environment and makes all the following tests fail.

I am using Django 1.1 with Ubuntu 10.04, Postgres 8.4 and psycopg2.

Does the problem still exist in Django 1.2?

Is it a known bug or am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

七秒鱼° 2024-09-23 17:00:26

Django 有两种类型的 TestCase:“plain”TestCaseTransactionTestCase文档有以下关于差异的说明他们两人之间:

TransactionTestCaseTestCase 完全相同,只是数据库重置为已知状态的方式以及测试代码测试提交和回滚效果的能力不同。 TransactionTestCase 在测试运行之前通过截断所有表并重新加载初始数据来重置数据库。 TransactionTestCase 可以调用提交和回滚,并观察这些调用对数据库的影响。

另一方面,TestCase 不会在测试开始时截断表并重新加载初始数据。相反,它将测试代码包含在数据库事务中,该事务在测试结束时回滚。

您正在使用 TransactionTestCase 来执行这些测试。切换到普通的TestCase,如果您维护现有的测试代码,您将看到问题消失。

为什么会出现这种情况? TestCase 在事务块内执行测试方法。这意味着测试类中的每个测试方法将在单独的事务而不是同一事务中运行。当断言(或者更确切地说是内部的 lambda )引发错误时,它会随事务一起终止。下一个测试方法是在新事务中执行的,因此您不会看到收到的错误。

但是,如果您要在相同测试方法中添加另一个相同的断言,您将再次看到错误:

class TransProblemTest(django.test.TestCase):
    def test_uniqueness1(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))
        # Repeat the test condition.
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

这是因为第一个断言将被触发创建导致事务中止的错误。因此第二个无法执行。由于两个断言都发生在同一测试方法内,因此与之前的情况不同,尚未启动新事务。

希望这有帮助。

Django has two flavors of TestCase: "plain" TestCase and TransactionTestCase. The documentation has the following to say about the difference between the two of them:

TransactionTestCase and TestCase are identical except for the manner in which the database is reset to a known state and the ability for test code to test the effects of commit and rollback. A TransactionTestCase resets the database before the test runs by truncating all tables and reloading initial data. A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.

A TestCase, on the other hand, does not truncate tables and reload initial data at the beginning of a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test.

You are using TransactionTestCase to execute these tests. Switch to plain TestCase and you will see the problem vanish provided you maintain the existing test code.

Why does this happen? TestCase executes test methods inside a transaction block. Which means that each test method in your test class will be run inside a separate transaction rather than the same transaction. When the assertion (or rather the lambda inside) raises an error it dies with the transaction. The next test method is executed in a new transaction and therefore you don't see the error you've been getting.

However if you were to add another identical assertion in the same test method you would see the error again:

class TransProblemTest(django.test.TestCase):
    def test_uniqueness1(self):
        User.objects.create_user(username='user1', email='[email protected]', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))
        # Repeat the test condition.
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='[email protected]', password='secret'))

This is triggered because the first assertion will create an error that causes the transaction to abort. The second cannot therefore execute. Since both assertions happen inside the same test method a new transaction has not been initiated unlike the previous case.

Hope this helps.

回眸一遍 2024-09-23 17:00:26

我假设当您说“单个测试方法有效”时,您的意思是它失败,引发异常,但不会破坏测试环境。

也就是说,您正在关闭 AutoCommit 的情况下运行。在该模式下,默认情况下共享数据库连接上的所有内容都是单个事务,出现故障时需要通过 ROLLBACK 中止事务,然后才能启动新事务。我建议如果可能的话,打开自动提交——除非您需要将多个写入操作包装到一个单元中,否则关闭它就太过分了。

I'm assuming when you say "a single test method works", you mean that it fails, raises the exception, but doesn't break the testing environment.

That said, you are running with AutoCommit turned off. In that mode, everything on the shared database connection is a single transaction by default and failures require the transaction to be aborted via ROLLBACK before a new one can be started. I recommend turning AutoCommit on, if possible--unless you have a need for wrapping multiple write operations into a single unit, having it off is overkill.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文