1

私が Django アプリケーション用に作成したテストは、SQLite を使用した初期開発中に完全に機能しています。展開する準備ができたので、MySQL サーバーをセットアップしました (これが展開先になります) が、テストの一部が失敗しています。

最後に、機能を手動でテストしても、失敗するテストは失敗しません。

何が起こっているのでしょうか?

私は何も変わったことをしていません。すべてのビューがデータベースの悪ふざけを行い、応答を返します。タイミングに関連するものは何もありません (スレッドなどはありません)。

テストはすべて django.test.TestCase から継承されており、フィクスチャは使用していません。

失敗するテストの例を次に示します。

class BaseTest(TestCase):
    def setUp(self):
        super(BaseTest, self).setUp()

        self.userCreds = dict(username='test', password='a')

        # Create an admin user
        admin = models.User.objects.create_superuser(
            email='', username='admin', password='a')

        # Create a user and grant them a licence
        user = models.User.objects.create_user(
            email='some@address.com', first_name="Mister",
            last_name="Testy", **self.userCreds)

        profile = models.getProfileFor(user)
        node = profile.createNode(
            '12345', 'acomputer', 'auser',
            'user@email.com', '0456 987 123')

        self.node = node

class TestClientUIViews(BaseTest):
    def test_toggleActive(self):
        url = reverse('toggleActive') + '?nodeId=%s' % self.node.nodeId

        self.assertFalse(self.node.active)

        # This should fail because only authenticated users can toggle a node active
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 403)
        self.assertFalse(self.node.active)

        # Login and make sure visiting the url toggles the active state
        self.client.login(**self.userCreds)
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertTrue(self.node.active)

        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertFalse(self.node.active)

モデルは次のようになります。

class Node(models.Model):
    @property
    def active(self):
        '''
        Activation state gets explictly tracked in its own table but is
        exposed as a property for the sake of convenience
        '''
        activations = NodeActivation.objects \
            .filter(node=self) \
            .order_by('-datetime')

        try:
            return activations[0].active
        except IndexError:
            return False

    @active.setter
    def active(self, state):
        if self.active != state:
            NodeActivation.objects.create(node=self, active=state)

class NodeActivation(models.Model):
    node = models.ForeignKey("Node")
    datetime = models.DateTimeField(default=datetimeM.datetime.now)
    active = models.BooleanField(default=False)

私のローカル MySQL は 5.5.19 (InnoDB を使用) ですが、5.1.56 を使用しているデプロイメント サーバーでも同じエラーが発生します。ストレージ エンジンに関係なく、テストは失敗します。

冒頭で述べたように、SQLite データベースを使用するように切り替えると、すべてのテストが合格に戻ります。

4

3 に答える 3

2

実際のコードが明らかになったので、このテストが失敗する理由について次の仮説を立てます。

NodeActivationモデルが正しくありません。datetimeフィールドは次のとおりです。

datetime = models.DateTimeField(auto_now=True)

datetime.datetime.now()モデル定義での使用は一度だけ評価されます。

NodeActivationセッターが新しいレコードを作成するたびに、レコードは同じ日付/時刻で作成されます。NodeActivationつまり、モデルが最初に評価された日時。

ゲッターは単一の結果のみを返します。ただし、両方のアクティベーション レコードの日付/時刻が同じであるため、順序付けはデータベース バックエンドに依存する場合があります。テストの最後にデータベースには2 つのNodeActivationレコードがあり、どちらが返されるかは不明です。

于 2013-02-05T06:14:04.593 に答える
1

Node モデル クラスの active プロパティを次のように変更します。

@property
def active(self):
    '''
    Activation state gets explictly tracked in its own table but is
    exposed as a property for the sake of convenience
    '''
    activations = NodeActivation.objects \
        .filter(node=self) \
        .order_by('-id')

    try:
        return activations[0].active
    except IndexError:
        return False

問題は解決します。

order_by通話の変更に注意してください。

レコードが非常に迅速に作成されたため、datetime による順序付けが決定論的ではなかったため、動作が不安定になりました。そして、SQLite は MySQL よりも遅いだけだと思います。そのため、バッキング データベースとして使用しても問題はありませんでした。

注:ヒントを提供してくれたAustin Phillipsに感謝します (彼の回答のコメントを確認してください)。

于 2013-02-05T07:14:34.857 に答える