20

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

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

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

  1. 创建一个新用户并为 X 天访问该站点生成发票

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

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

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

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

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

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

  4. 检查是否已开具新发票

这有点笨拙,但我认为它可能会起作用。关于如何完成它的任何其他想法?

4

3 回答 3

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,所以我们创建了一个假的 datetime 类,它以相同的方式执行所有操作,除了在调用 now 时返回一个常量值。

于 2012-05-18T12:29:12.897 回答
4

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

我会否决更改系统时间。这看起来不像是单元测试,而是像功能测试,因为它不能与该机器上的其他任何东西并行完成。

于 2012-05-18T13:28:41.980 回答
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)
于 2020-04-12T15:35:05.410 回答