django测试中的模拟时间问题:时间似乎不能使用freezegun冻结

Dhi*_*aTN 6 python django unit-testing django-rest-framework freezegun

我在这里写一个功能测试来检查我的API限制是否按预期工作(将在每个月的开始时休息).

测试类:

class ApiThrottlingTest(ThrottlingBaseTest):

    def test_throttling_purchaser_case(self):

        now = datetime.datetime(year=2015, month=1, day=10, hour=6, minute=6, second=3)

        last_day_of_current_month = datetime.datetime(year=2015, month=1, day=31, hour=23, minute=59, second=59)

        first_day_of_next_month = datetime.datetime(year=2015, month=2, day=1, hour=0, minute=0, second=0)

        with freeze_time(now) as frozen_datetime:
            for i in xrange(3):
                resp = self._project_details_request()
                self.assertEqual(resp.status_code, 200)

            resp = self._project_details_request()
            self.assertEqual(resp.status_code, 429)

            frozen_datetime.move_to(last_day_of_current_month)
            resp = self._project_details_request()
            # the test fails at this level
            self.assertEqual(resp.status_code, 429)

            frozen_datetime.move_to(first_day_of_next_month)
            resp = self._project_details_request()
            self.assertEqual(resp.status_code, 200)
Run Code Online (Sandbox Code Playgroud)

如果出现以下情况,测试工作正常:last_day_of_current_month = datetime.datetime(... second=0)
但如果出现以last_day_of_current_month = datetime.datetime(... second=59)

在调试之后,似乎timeDjangoRestFramework throttling.UserRateThrottle中使用的模块在某种程度上给出了一个值,该值总是超过我测试中的前一个时间,这导致了几秒钟的精度问题.

基于FreezeGun Doc time.time()也应冻结:

一旦调用了装饰器或上下文管理器,所有调用datetime.datetime.now(),datetime.datetime.utcnow(),datetime.date.today(),time.time(),time.localtime(),time .gmtime()和time.strftime()将返回已冻结的时间.

但看起来我的情况time.time正确地取得了模拟日期时间的开始时间,但随后随着时间的推移不断变化,这是预期的,它会被冻结,直到手动转发时间.

我试图使用模块单独使用模拟time.time,但仍然没有解决问题.UserRateThrottlemock

---->任何想法可能是什么问题,怎么可能解决?

测试失败:(在时间转发到该月的最后一天之后:第14行)

self.assertEqual(resp.status_code,429)
AssertionError:200!= 429

DRF类源代码:

class SimpleRateThrottle(BaseThrottle):
    ...

    cache = default_cache
    timer = time.time 
    cache_format = 'throttle_%(scope)s_%(ident)s'

    def __init__(self):
       ....

    def allow_request(self, request, view):
        ...

        self.now = self.timer() # here timer() returns unexpected value in test
        ....
Run Code Online (Sandbox Code Playgroud)

Lin*_*via 2

你需要覆盖SimpleRateThrottle使用 FreezeGun 的 time.time 覆盖 的计时器。

这里发生的情况是,fezegun 可能会覆盖 Python 的时间模块。但是,SimpleRateThrottle不使用该模块,而是使用该模块的功能,而该模块的功能变得超出了冷冻枪的范围。

SimpleRateThrottle因此使用Python标准库时间模块,而代码的其他部分使用freezegun的时间模块。

编辑:您应该在 FreezeGun 激活后执行以下操作:

former_timer = SimpleRateThrottle.timer
SimpleRateThrottle.timer = time.time
Run Code Online (Sandbox Code Playgroud)

一旦你的测试结束(在拆解或其他类似的情况下):

SimpleRateThrottle.timer = former_timer
Run Code Online (Sandbox Code Playgroud)

请注意,您还可以使用猴子修补库来为您处理该问题。