测试计划库 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 函数!它只被调用一次,然后程序退出。原因是,我们只是将 timeschedule 模块中的函数名称重新绑定到我们的 "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