测试计划库 python - 时间和事件
Testing schedule library python - Time and Events
假设我在 Ubuntu 远程服务器上使用计划库每 3 天触发一次事件。
代码应该类似于这样:
import schedule
import time
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
如何加快我的时钟速度或测试此代码?
您可以用不同的方式测试此代码,而不是伪造时钟。
显然,您可以为 job()
函数编写单元测试(即确保它正在做它应该做的事情)。这对你来说可能是显而易见的。您可能想知道如何测试主脚本是否会正确调用您的函数,但是当您 运行 测试时您无法确保它是 10:30。
进入著名的"monkey patch"。由于 Python 有第一个 class 函数,您可以简单地将名称绑定到函数。我不会深入探讨单元测试框架以及如何使用 mock
库,但这里有一个您可能需要的快速示例:
import schedule
import time
def mock_run_pending():
job()
def mock_time_sleep(num):
exit()
schedule.run_pending = mock_run_pending
time.sleep = mock_time_sleep
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
那么这里发生了什么? 如果您 运行 代码片段,您会注意到实际上调用了 job
函数!它只被调用一次,然后程序退出。原因是,我们只是将 time
和 schedule
模块中的函数名称重新绑定到我们的 "mock" 版本(使它们更易于测试)。
编辑:我们可以更疯狂地测试我们是否将正确的参数传递给调度程序(我忍不住自己):
import schedule
import time
class MockEvery(object):
def __init__(self, num):
assert(num == 3)
self.days = MockDays()
class MockDays(object):
def __init__(self):
pass
def at(self, time):
assert(time == "10:30")
return MockAt()
class MockAt(object):
def __init__(self):
pass
def do(self, func):
assert(func == job)
def mock_every(num):
return MockEvery(num)
def mock_run_pending():
job()
def mock_time_sleep(num):
exit()
schedule.run_pending = mock_run_pending
time.sleep = mock_time_sleep
schedule.every = mock_every
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
P.S。 (这与您的问题有些无关,但您可能会发现它很有用):如果您希望将其合并到测试框架中,请查看 mock
包,尤其是 @patch
装饰器。它允许您在单元测试(或与此相关的任何函数)内部修改补丁函数。如果您有其他测试,您也不想调用 exit()
。使用 pytest
很容易退出特定测试,使用内置 unittest
则有点困难,但肯定有可能。但是,我跑题了。
HTH.
作为参考,也可以使用 freezegun 库来伪造时钟:
from datetime import datetime, timedelta
import schedule
import freezegun
def job():
print("I'm working...")
now = datetime(2020, 1, 1, 10, 31)
with freezegun.freeze_time(now) as frozen_date:
schedule.every(3).days.at("10:30").do(job)
schedule.run_pending() # nothing happens
frozen_date.move_to(now + timedelta(days=3))
schedule.run_pending() # job has run
假设我在 Ubuntu 远程服务器上使用计划库每 3 天触发一次事件。
代码应该类似于这样:
import schedule
import time
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
如何加快我的时钟速度或测试此代码?
您可以用不同的方式测试此代码,而不是伪造时钟。
显然,您可以为 job()
函数编写单元测试(即确保它正在做它应该做的事情)。这对你来说可能是显而易见的。您可能想知道如何测试主脚本是否会正确调用您的函数,但是当您 运行 测试时您无法确保它是 10:30。
进入著名的"monkey patch"。由于 Python 有第一个 class 函数,您可以简单地将名称绑定到函数。我不会深入探讨单元测试框架以及如何使用 mock
库,但这里有一个您可能需要的快速示例:
import schedule
import time
def mock_run_pending():
job()
def mock_time_sleep(num):
exit()
schedule.run_pending = mock_run_pending
time.sleep = mock_time_sleep
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
那么这里发生了什么? 如果您 运行 代码片段,您会注意到实际上调用了 job
函数!它只被调用一次,然后程序退出。原因是,我们只是将 time
和 schedule
模块中的函数名称重新绑定到我们的 "mock" 版本(使它们更易于测试)。
编辑:我们可以更疯狂地测试我们是否将正确的参数传递给调度程序(我忍不住自己):
import schedule
import time
class MockEvery(object):
def __init__(self, num):
assert(num == 3)
self.days = MockDays()
class MockDays(object):
def __init__(self):
pass
def at(self, time):
assert(time == "10:30")
return MockAt()
class MockAt(object):
def __init__(self):
pass
def do(self, func):
assert(func == job)
def mock_every(num):
return MockEvery(num)
def mock_run_pending():
job()
def mock_time_sleep(num):
exit()
schedule.run_pending = mock_run_pending
time.sleep = mock_time_sleep
schedule.every = mock_every
def job():
print("I'm working...")
schedule.every(3).days.at("10:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
P.S。 (这与您的问题有些无关,但您可能会发现它很有用):如果您希望将其合并到测试框架中,请查看 mock
包,尤其是 @patch
装饰器。它允许您在单元测试(或与此相关的任何函数)内部修改补丁函数。如果您有其他测试,您也不想调用 exit()
。使用 pytest
很容易退出特定测试,使用内置 unittest
则有点困难,但肯定有可能。但是,我跑题了。
HTH.
作为参考,也可以使用 freezegun 库来伪造时钟:
from datetime import datetime, timedelta
import schedule
import freezegun
def job():
print("I'm working...")
now = datetime(2020, 1, 1, 10, 31)
with freezegun.freeze_time(now) as frozen_date:
schedule.every(3).days.at("10:30").do(job)
schedule.run_pending() # nothing happens
frozen_date.move_to(now + timedelta(days=3))
schedule.run_pending() # job has run