为单元测试覆盖auto_now

9 投票
3 回答
3577 浏览
提问于 2025-04-15 19:21

我在数据库里为一些事件定义了时间戳,使用的是 auto_now_add,这样事件存储的时候就会自动记录下时间。

这些事件的描述大概是这样的:

class NewEvent(models.Model):
    '''
    Individual event
    '''
    name = models.CharField(max_length=100)
    quantity = models.FloatField(null=True)
    timestamp = models.DateTimeField(auto_now_add=True)

为了测试这个模块,我在 test.py 文件里生成了一些数据库信息,方法是:

    for event in EVENT_TYPES:
        time = datetime.datetime.now() - datetime.timedelta(days=1)
        for i in range(48):
            time = time.replace(hour=i / 2)
            NewEvent(name=event,
                     timestamp=time,
                     quantity=i).save()

我需要生成一些事件,时间戳是昨天的(这个模块会对这些事件进行汇总)。问题是,时间戳是不能被覆盖的。时间戳就是事件发生的时间,文档里说得很清楚。

那么,怎么才能生成带有合适时间戳的数据来进行测试呢?我有几个想法:

  • 也许可以用不同的方式生成数据库数据,放在模型类之外。那应该在哪里做呢?
  • 或者定义一个不同的类,或者在测试时让这个类表现得不一样,类似于:

_

 if testing:
     timestamp = models.DateTimeField(auto_now_add=True)
 else:
     timestamp = models.DateTimeField(auto_now_add=False)

或者说,也许还有更简单的方法来做到这一点……有什么想法吗?

3 个回答

3

对我来说,使用测试数据的一个问题是,我需要检查一些超过30天的记录不会被返回,而那些不满30天的记录会被返回……使用静态测试数据,这个要求很难实现(比较懒的方式)。所以我选择了模拟django用来获取当前时间的timezone.now函数。

from django.utils import timezone 

class SomeTestCase(TestCase):
    def test_auto_add(self):
        now = timezone.now()
        now_31 = now - datetime.timedelta(days=31)
        self.mock('timezone.now', returns=now_31, tracker=None)
        SomeObject.objects.create() # has auto_now_add field ...   

在模拟的过程中,我使用了minimocktest这个工具。

7

处理这个问题的另一种方法是,在创建实例后使用QuerySet的update,这可能会根据你的具体情况更有用。

因为update是在SQL层面执行的,所以它会跳过验证、信号和自定义保存功能。这意味着它会需要额外的数据库调用,这可能会影响性能,所以使用时要考虑清楚。

for event in EVENT_TYPES:
    time = datetime.datetime.now() - datetime.timedelta(days=1)
    for i in range(48):
        time = time.replace(hour=i / 2)
        instance = NewEvent(name=event, quantity=i).save()
        NewEvent.objects.filter(pk=instance.pk).update(timestamp=time)
2

我成功地用一个固定数据文件覆盖了默认值。

我创建了一个 test_data.json 文件,里面的数据格式如下:

[
{
    "model": "stats_agg.newevent",
    "pk": 1,
    "fields": {
        "name": "event1",
        "quantity":0.0,
        "timestamp": "2010-02-15 00:27:40"
     }
},
{
    "model": "stats_agg.newevent",
    "pk": 2,
    "fields": {
        "name": "event1",
        "quantity":1.0,
        "timestamp": "2010-02-15 00:27:40"
     }
},
...

然后我把它添加到了测试单元中。

class SimpleTest(TestCase):
   fixtures = ['test_data.json']

撰写回答