问题
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