Check if Timer.cancel is called in unit test

回眸只為那壹抹淺笑 提交于 2020-06-27 15:45:13

问题


I'm using the threading.Timer package to execute a method after x seconds. However, in some cases I want to execute this method earlier and cancel the timer (so it isn't called twice). How do I unit test this?

I want to know if the timer has stopped so that the method is not called anymore. I am now using the following code, unfortunately the is_alive still returns 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())

Form the documentation the is_alive method returns True during the run operation;

Return whether the thread is alive. This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads.

The documentation on the cancel method says the following;

Stop the timer, and cancel the execution of the timer’s action. This will only work if the timer is still in its waiting stage.

Does this mean that the cancel method does not stop the run action? Or is is still in the grey area after the run method and returns True for that reason?


回答1:


With timer.is_alive() you are just checking if the timer-thread itself is alive, so if you want to "check if timer.cancel() was called", you're testing for the wrong thing.

Does this mean that the cancel method does not stop the run action?

It does not stop the run()-function, right. timer.cancel() just sets a flag in an Event-object which gets checked by run. You can test if the flag is set with:

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

Unfortunately, checking for cancellation is not enough to prevent repeated execution, since run can have already crossed the check like you can see in the source code:

# 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()

Some more effort is needed to ensure unique execution. I've written up a possible solution to this in my answer here.



来源:https://stackoverflow.com/questions/56441592/check-if-timer-cancel-is-called-in-unit-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!