模拟单元测试中的时间流逝

时间:2012-05-18 11:48:38

标签: python testing mocking integration-testing celery

我为客户建立了一个付费的CMS +发票系统,我的测试需要更加严格。

我将所有数据保存在Django ORM中,并且有一堆Celery任务以不同的时间间隔运行,以确保在用户不支付发票时发送新发票和发票提醒并减少访问权限。

例如,我希望能够运行以下测试:

  1. 创建新用户并为X天访问网站生成发票

  2. 模拟X + 1天的传递,并运行我在Celery中设置的所有任务。

  3. 检查是否已向用户发出了其他X天的新发票。

  4. 到目前为止,我提出的KISS方法是在一台单独的机器上进行所有测试,并实际操作操作系统级别的日期/时间。所以测试脚本会:

    1. 将系统日期设置为第1天

    2. 创建新用户并为X天访问生成第一张发票

    3. 提前1天系统日期。运行我所有的芹菜任务。重复直到X + 1天“通过”

    4. 检查是否已签发新发票

    5. 它有点笨重,但我认为它可能会奏效。关于如何完成它的任何其他想法?

3 个答案:

答案 0 :(得分:18)

您可以使用mock更改用于获取时间的函数的返回值(例如datetime.datetime.now)。

有多种方法可以这样做(参见模拟文档),但这里有一个:

import unittest
import datetime
from mock import patch

class SomeTestCase(unittest.TestCase):
    def setUp(self):
        self.time = datetime.datetime(2012, 5, 18)
        class fakedatetime(datetime.datetime):
            @classmethod
            def now(cls):
                return self.time
        patcher = patch('datetime.datetime', fakedatetime)
        self.addCleanup(patcher.stop)
        patcher.start()

    def test_something(self):
        self.assertEqual(datetime.datetime.now(), datetime.datetime(2012, 5, 18))
        self.time = datetime.datetime(2012, 5, 20)
        self.assertEqual(datetime.datetime.now(), datetime.datetime(2012, 5, 20))

因为我们无法直接替换datetime.datetime.now,所以我们创建了一个假日期时间类,它以相同的方式执行所有操作,除非在调用now时返回常量值。

答案 1 :(得分:4)

如果不使用特殊的模拟库,我建议准备处于模拟模式的代码(可能是通过全局变量)。在模拟模式中,而不是调用正常的时间函数(如time.time()或其他),你可以调用一个模拟时间函数,它返回你特殊情况下你需要的任何东西。

我会投票决定更改系统时间。这似乎不是一个单元测试,而是一个功能测试,因为它无法与该机器上的任何其他内容并行完成。

答案 2 :(得分:1)

您还可以查看freezegun模块。 Github-https://github.com/spulec/freezegun

从他们的文档中

from freezegun import freeze_time
import datetime


@freeze_time("2012-01-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)