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'))

単一のテスト メソッドを持つテスト クラスは機能しますが、2 つの同一のメソッド実装では失敗します。最初のテストで例外がスローされると、Django テスト環境が破壊され、以降のすべてのテストが失敗します。

Ubuntu 10.04、Postgres 8.4、psycopg2 で Django 1.1 を使用しています。

問題は Django 1.2 でもまだ存在しますか?

それは既知のバグですか、それとも何か不足していますか?

4

2 に答える 2

5

TestCaseDjangoには、 「プレーン」TestCaseと。の2つのフレーバーがありTransactionTestCaseます。ドキュメントには、2つの違いについて次のように書かれています。

TransactionTestCaseデータベースが既知の状態にリセットされる方法とTestCase、テストコードがコミットとロールバックの効果をテストする機能を除いて、同じです。TransactionTestCaseリセットは、テストが実行される前に、すべてのテーブルを切り捨てて初期データを再ロードすることにより、データベースをリセットします。ATransactionTestCaseは、コミットとロールバックを呼び出して、データベースに対するこれらの呼び出しの影響を観察できます。

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'))

これがトリガーされるのは、最初のアサーションによってエラーが発生し、トランザクションが中止されるためです。したがって、2番目は実行できません。両方のアサーションが同じテストメソッド内で発生するため、前のケースとは異なり、新しいトランザクションは開始されていません。

お役に立てれば。

于 2010-08-30T16:36:40.520 に答える
1

「単一のテスト方法が機能する」と言うとき、それは失敗し、例外を発生させますが、テスト環境を壊さないことを意味すると思います。

そうは言っても、AutoCommitをオフにして実行しています。このモードでは、共有データベース接続上のすべてがデフォルトで単一のトランザクションであり、障害が発生した場合は、新しいトランザクションを開始する前に、ロールバックを介してトランザクションを中止する必要があります。可能であれば、自動コミットをオンにすることをお勧めします。複数の書き込み操作を1つのユニットにラップする必要がない限り、オフにするのはやり過ぎです。

于 2010-08-30T14:56:57.990 に答える