我在这里写了一个功能测试来检查我的 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)
测试在以下情况下工作正常:last_day_of_current_month = datetime.datetime(... second=0)
但在以下情况下将失败:last_day_of_current_month = datetime.datetime(... second=59)
调试后,似乎time
DjangoRestFramework 中使用的模块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
使用,但仍然没有解决问题。UserRateThrottle
mock
----> 知道可能是什么问题,如何解决?
测试失败:(将时间转发到该月的最后一天:第 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
....