检查是否在单元测试中调用了Timer.cancel

时间:2019-06-04 09:58:34

标签: python python-multithreading python-unittest

我正在使用threading.Timer包在x秒后执行一个方法。但是,在某些情况下,我想更早地执行此方法并取消计时器(因此不会被调用两次)。 如何对此进行单元测试?

我想知道计时器是否已停止,以便不再调用该方法。我现在正在使用以下代码,不幸的是is_alive still返回True

from threading import Timer

Class X():
    def __init__(self, timeout):
        self.timer = Timer(timeout, self.some_method)
        self.timer.start()

    def some_method(self):
        # Do something

    def other_method(self):
        self.timer.cancel()
        self.some_method()

import unittest

Class TestX(unittest.TestCase):
    def test_cancel_timer(self):
        x = X(1000)
        x.other_method()
        self.assertFalse(x.timer.is_alive())

在文档中,is_alive方法在run操作期间返回True;

  

返回线程是否处于活动状态。   该方法在run()方法开始之前直到run()方法终止之后才返回True。模块函数enumerate()返回所有活动线程的列表。

关于cancel方法的文档中的内容如下:

  

停止计时器,并取消执行计时器的操作。仅当计时器仍处于等待状态时,此功能才起作用。

这是否意味着cancel方法不会停止run操作?还是在run方法之后仍处于灰色区域并因此返回True?

1 个答案:

答案 0 :(得分:1)

使用timer.is_alive()时,您只是在检查计时器线程本身是否还处于活动状态,因此,如果要“检查是否调用了timer.cancel()”,则说明正在测试错误的内容。

  

这是否意味着cancel方法不会停止运行操作?

它不会停止run()功能。 timer.cancel()只是在Event对象中设置一个标志,run会对其进行检查。您可以测试标志是否设置为:

self.assertTrue(x.timer.finished.is_set())

不幸的是,检查取消还不足以防止重复执行,因为run可能已经通过了检查,就像您在源代码中看到的那样:

# threading.py (Python 3.7.1):

class Timer(Thread):
    """Call a function after a specified number of seconds:

            t = Timer(30.0, f, args=None, kwargs=None)
            t.start()
            t.cancel()     # stop the timer's action if it's still waiting

    """

    def __init__(self, interval, function, args=None, kwargs=None):
        Thread.__init__(self)
        self.interval = interval
        self.function = function
        self.args = args if args is not None else []
        self.kwargs = kwargs if kwargs is not None else {}
        self.finished = Event()

    def cancel(self):
        """Stop the timer if it hasn't finished yet."""
        self.finished.set()

    def run(self):
        self.finished.wait(self.interval)
        if not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
        self.finished.set()

需要更多的精力来确保唯一的执行。我已经在答案here中写出了一个可能的解决方案。