如何清理在django-nose测试函数中所做的数据库更改?

1 投票
1 回答
1092 浏览
提问于 2025-04-17 16:39

我们使用nose功能测试来编写我们的测试套件,原因有很多。

在运行我们的Django应用程序的测试套件时,我们希望避免测试中泄露任何数据(就像使用django.test.TestCase那样),因为这会导致代码之间的耦合,进而出现难以诊断的错误。

解决这个问题最明显的方法是使用一个装饰器,我们可以把它包裹在需要清理的测试周围,但如果有其他解决方案能达到我们的目的,我也不一定非要用这个。

我们使用的是PostgreSQL,所以Postgres特定的解决方案也可以。

1 个回答

1

今天我花了一些时间研究这个问题,得出了一个装饰器的实现:

from functools import wraps

from django.db import transaction
from mock import patch

def rollback_db_changes(func):
    """Decorate a function so that it will be rolled back once completed."""
    @wraps(func)
    @transaction.commit_manually
    def new_f(*args, **kwargs):
        def fake_commit(using=None):
            # Don't properly commit the transaction, so we can roll it back
            transaction.set_clean(using)
        patcher = patch('django.db.transaction.commit', fake_commit)
        patcher.start()
        try:
            return func(*args, **kwargs)
        finally:
            patcher.stop()
            transaction.rollback()
    return new_f

我们进行这个补丁处理,是为了让Django的测试客户端在测试过程中不会关闭事务,这样我们就可以在需要的时候把它回滚。这使得后面的测试能够顺利通过:

from django.contrib.auth.models import User

@rollback_db_changes
def test_allowed_access():
    user = User.objects.create(username='test_user')
    eq_(1, User.objects.count())


@rollback_db_changes
def test_allowed_access_2():
    user = User.objects.create(username='test_user')
    eq_(1, User.objects.count())

之前,第二个测试运行时无法创建一个用户名重复的用户。

撰写回答