说‘不道德!`冻结你的测试
immobilus的Python项目详细描述
一个简单的python测试时间冻结工具。它嘲笑:
- datetime.date.today()
- datetime.datetime.now()
- datetime.datetime.utcnow()
- datetime.datetime.fromtimestamp()
- time.time()
- time.gmtime()
- time.localtime()
- time.strftime()
- time.mktime()
用法
您必须在datetime或time或任何 依次导入它们的其他模块,以允许它拦截那些 模块。
>>>fromimmobilusimportimmobilus>>>fromdatetimeimportdatetime,timedelta
例如,如果使用 pytest,您可以添加 import immobilus进入根文件conftest.py。
上下文管理器
您可以使用immobilus作为上下文管理器。当上下文管理器 处于活动状态,时间将冻结到指定值。上下文之外 经理,使用原始的标准库函数 行为正常。
>>># It is unlikely that you are living in the past>>>datetime.utcnow()==datetime(2017,10,20)False>>># But with immobilus, you can pretend that you are>>>withimmobilus('2017-10-20'):...datetime.utcnow()==datetime(2017,10,20)...True>>># Once the context manager exits, immobilus deactivates.>>># We are back in the present.>>>datetime.utcnow()==datetime(2017,10,20)False
指定冻结时间
如上所示,可以使用字符串来描述要冻结的时间 (例如'2017-10-20')。任何价值观 dateutil.parser 可以使用。
您还可以使用datetime.datetime对象作为冻结时间:
>>>naive_freeze_time=datetime(2017,10,20)>>>withimmobilus(naive_freeze_time):...print('now: %s'%datetime.now())...print('utcnow: %s'%datetime.utcnow())...now:2017-10-2000:00:00utcnow:2017-10-2000:00:00
immobilus将使用给定的datetime对象设置冻结 UTC时间和本地时间与您提供的值相同。如果 datetime您提供的冻结时间是已知的,那么它是 调整为UTC,如下所示:
>>>importpytz>>>>>># Freeze to 12:00 noon in Moscow (UTC+3)>>>timezone=pytz.timezone('Europe/Moscow')>>>aware_freeze_time=timezone.localize(datetime(2017,10,20,12))>>>withimmobilus(aware_freeze_time):...# 9:00am local time which is same as UTC...print('now: %s'%datetime.now())...print('utcnow: %s'%datetime.utcnow())...now:2017-10-2009:00:00utcnow:2017-10-2009:00:00
如果希望本地时间与UTC时间不同,请继续阅读。
冻结不是UTC时间的本地时间
若要在时间冻结时使其他时区生效,请使用 immobilus上下文管理器的第二个参数:tz_offset。 这是冻结的UTC时间之前的小时数 当地时间应该是。
>>>withimmobilus('2017-10-20 09:00',tz_offset=3):...print('now: %s'%datetime.now())...print('utcnow: %s'%datetime.utcnow())...now:2017-10-2012:00:00utcnow:2017-10-2009:00:00
当然,如果您愿意,可以使用否定的 号码:
>>>withimmobilus('2017-10-20 09:00',tz_offset=-7):...print('now: %s'%datetime.now())...print('utcnow: %s'%datetime.utcnow())...now:2017-10-2002:00:00utcnow:2017-10-2009:00:00
您可以通过调用tick方法来移动冻结的时间点:
>>>withimmobilus('2019-08-21 12:00:00')asdt:...print(datetime.now())...dt.tick()...print(datetime.now())...dt.tick(timedelta(seconds=10))...print(datetime.now())...2019-08-2112:00:002019-08-2112:00:012019-08-2112:00:11
用作装饰
除了作为上下文管理器,immobilus也是一个装饰器:
>>>@immobilus('2017-10-20')...deftest():...print(datetime.now())...>>>test()2017-10-2000:00:00
它甚至适用于类
>>>@immobilus('2017-10-20')...classDecorated(object):...now=datetime.utcnow()......deffirst(self):...returndatetime.utcnow()......defsecond(self):...returnself.now...>>>d=Decorated()>>>assertd.first().strftime('%Y-%m-%d %H:%M:%S')=='2017-10-20 00:00:00'>>>assertd.second().strftime('%Y-%m-%d %H:%M:%S')!='2017-10-20 00:00:00'
和协作(从python 3.5开始)
>>>importsys>>>importsix>>>>>>ifsys.version_info[0:2]>=(3,5):...result=''...six.exec_(""" ... import asyncio ... ... @immobilus('2017-10-20') ... async def test(): ... return datetime.now() ... ... loop = asyncio.new_event_loop() ... result = loop.run_until_complete(test()) ... """)...assertresult.strftime('%Y-%m-%d %H:%M:%S')=='2017-10-20 00:00:00'
直接使用
或者您可以手动激活和停用immobilus。
>>>freeze_time=datetime(2017,10,20)>>>spell=immobilus(freeze_time)>>>datetime.utcnow()==freeze_timeFalse>>>spell.start()FakeDatetime(2017,10,20,0,0)>>>datetime.utcnow()==freeze_timeTrue>>>datetime.utcnow()FakeDatetime(2017,10,20,0,0)>>>spell.stop()>>>datetime.utcnow()==freeze_timeFalse
这对使用标准库的用户非常有用 unittest.TestCase例如
importunittestclassSomeTests(unittest.TestCase):defsetUp(self):spell=immobilus('2017-10-20')spell.start()self.addCleanup(spell.stop)
嵌套
您还可以嵌套上下文管理器(或装饰器,或直接 调用,或任何组合)如果要冻结不同的时间。
>>>withimmobilus('2017-10-20 12:00'):...print('outer now: %s'%datetime.now())...print('outer utcnow: %s'%datetime.utcnow())...withimmobilus('2017-10-21 12:00',tz_offset=5):...print('inner now: %s'%datetime.now())...print('inner utcnow: %s'%datetime.utcnow())...print('outer now: %s'%datetime.now())...print('outer utcnow: %s'%datetime.utcnow())...outernow:2017-10-2012:00:00outerutcnow:2017-10-2012:00:00innernow:2017-10-2117:00:00innerutcnow:2017-10-2112:00:00outernow:2017-10-2012:00:00outerutcnow:2017-10-2012:00:00