首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在Python中更改所有模块的日期/时间?

如何在Python中更改所有模块的日期/时间?
EN

Stack Overflow用户
提问于 2010-04-17 18:22:31
回答 6查看 11.5K关注 0票数 24

当我使用业务逻辑编写代码时,我的代码通常依赖于当前时间。例如,算法查看每个未完成的订单,并检查是否应该发送发票(这取决于作业结束后的天数)。在这些情况下,创建发票不是由显式用户操作触发的,而是由后台作业触发的。

现在,当涉及到测试时,这给我带来了一个问题:

  • 我可以轻松地测试发票创建本身
  • ,但是很难在测试中创建订单并检查后台作业是否在正确的时间标识了正确的订单。

到目前为止,我找到了两个解决方案:

  • 在测试设置中,计算相对于当前日期的作业日期。缺点:代码变得相当复杂,因为不再有明确的日期写入。有时业务逻辑对于边缘情况来说是相当复杂的,因此由于所有这些相对日期,它变得很难调试。
  • 我有我自己的日期/时间访问器函数,我在代码中使用它。在测试中,我只设置了一个当前日期,所有模块都会得到这个日期。因此,我可以模拟2月份的订单创建,并检查发票是否在4月份轻松创建。缺点:第三方模块不使用这种机制,所以很难integrate+test这些。

毕竟,第二种方法对我来说要成功得多。因此,我正在寻找一种方法来设置Python的datetime+time模块返回的时间。设置日期通常就足够了,我不需要设置当前的小时或秒(尽管这样会很好)。

有这样的实用工具吗?有没有可以使用的(内部) Python API?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2017-07-13 04:04:25

freezegun包就是专门为此目的而开发的。它允许您更改测试代码的日期。它可以直接使用,也可以通过装饰器或上下文管理器使用。举个例子:

代码语言:javascript
复制
from freezegun import freeze_time
import datetime

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

有关更多示例,请参阅项目:https://github.com/spulec/freezegun

票数 10
EN

Stack Overflow用户

发布于 2010-04-18 02:19:49

实际上,为time.time打补丁可能就足够了,因为它为Python中几乎所有其他基于时间的例程提供了基础。这似乎很好地处理了您的用例,而不需要求助于更复杂的技巧,而且当您这样做时也无关紧要(除了像Queue.py和threading.py这样的少数执行from time import time的stdlib包,在这种情况下,您必须在导入它们之前打补丁):

代码语言:javascript
复制
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2010, 4, 17, 14, 5, 35, 642000)
>>> import time
>>> def mytime(): return 120000000.0
...
>>> time.time = mytime
>>> datetime.datetime.now()
datetime.datetime(1973, 10, 20, 17, 20)

也就是说,在为各种类型的自动化测试模拟对象的多年中,我很少需要这种方法,因为大多数情况下,需要模拟的是我自己的应用程序代码,而不是stdlib例程。毕竟,你知道它们已经起作用了。如果您遇到自己的代码必须处理库例程返回的值的情况,您可能希望模拟库例程本身,至少在检查您自己的应用程序将如何处理时间戳时是这样。

到目前为止,最好的方法是构建您自己的日期/时间服务例程,只在您的应用程序代码中使用,并内置测试根据需要提供假结果的能力。例如,我有时会做一个更复杂的等价物:

代码语言:javascript
复制
# in file apptime.py (for example)
import time as _time

class MyTimeService(object):
    def __init__(self, get_time=None):
        self.get_time = get_time or _time.time

    def __call__(self):
        return self.get_time()

time = MyTimeService()

现在在我的应用程序代码中,我只是执行import apptime as time; time.time()来获取当前时间值,而在测试代码中,我可以首先在我的setUp()代码中执行apptime.time = MyTimeService(mock_time_func),以提供假的时间结果。

更新:几年后,正如Dave Forgac's answer中指出的那样,有了另一种选择。

票数 12
EN

Stack Overflow用户

发布于 2010-04-17 20:36:15

您可以通过创建一个自定义的datetime模块(甚至是一个假的-请参见下面的示例)作为代理来修补系统,然后将其插入到sys.modules字典中。从那时起,每次导入到datetime模块都将返回您的代理。

datetime类仍然需要注意,特别是当有人执行from datetime import datetime时;为此,您只需为该类添加另一个代理即可。

这是我所说的一个例子--当然,这只是我在5分钟内抛出的东西,可能有几个问题(例如,datetime类的类型不正确);但希望它已经有用了。

代码语言:javascript
复制
import sys
import datetime as datetime_orig

class DummyDateTimeModule(sys.__class__):
  """ Dummy class, for faking datetime module """
  def __init__(self):
    sys.modules["datetime"] = self
  def __getattr__(self, attr):
    if attr=="datetime":
      return DummyDateTimeClass()
    else:
      return getattr(datetime_orig, attr)

class DummyDateTimeClass(object):
  def __getattr__(self, attr):
    return getattr(datetime_orig.datetime, attr)

dt_fake = DummyDateTimeModule()

最后--它值得吗?

坦率地说,我更喜欢我们的第二个解决方案,而不是这个:-)。

是的,python是一种非常动态的语言,你可以做很多有趣的事情,但是以这种方式修补代码总是有一定程度的风险,即使我们在这里谈论的是测试代码。

但最重要的是,我认为附件函数将使测试补丁更加明确,而且您的代码将在将要测试的内容方面更加明确,从而提高可读性。

因此,如果更改不是太昂贵,我会采用您的第二种方法。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2658026

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档