2

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

虽然我的测试和测试装置非常复杂,但我能够将问题追溯到以下示例,该示例不使用任何自定义模型。要重现该行为,只需将代码保存到 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='user1@example.com', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='user1@example.com', password='secret'))

    def test_uniqueness2(self):
        User.objects.create_user(username='user1', email='user1@example.com', password='secret')
        self.assertRaises(IntegrityError, lambda :
            User.objects.create_user(username='user1', email='user1@example.com', password='secret'))

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

我将 Django 1.1 与 Ubuntu 10.04、Postgres 8.4 和 psycopg2 一起使用。

这个问题在 Django 1.2 中是否仍然存在?

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

4

2 回答 2

5

Django 有两种风格TestCase:“普通”TestCaseTransactionTestCase. 该文档对它们两者之间的区别进行了以下说明:

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

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

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

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

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

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

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

希望这可以帮助。

于 2010-08-30T16:36:40.520 回答
1

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

也就是说,您在 AutoCommit 关闭的情况下运行。在这种模式下,默认情况下,共享数据库连接上的所有内容都是单个事务,失败需要通过 ROLLBACK 中止事务,然后才能启动新事务。如果可能,我建议打开 AutoCommit——除非您需要将多个写入操作包装到一个单元中,否则将其关闭是多余的。

于 2010-08-30T14:56:57.990 回答