How to specify test timeout for python unittest?
Question:
I’m using python
framework unittest
. Is it possible to specify by framework’s abilities a timeout for test? If no, is it possible to specify gracefully a timeout
for all tests and for some separated tests a private value for each one?
I want to define a global timeout
for all tests (they will use it by default) and a timeout for some test that can take a long time.
Answers:
As far as I know unittest
does not contain any support for tests timeout.
You can try timeout-decorator
library from PyPI. Apply the decorator on individual tests to make them terminate if they take too long:
import timeout_decorator
class TestCaseWithTimeouts(unittest.TestCase):
# ... whatever ...
@timeout_decorator.timeout(LOCAL_TIMEOUT)
def test_that_can_take_too_long(self):
sleep(float('inf'))
# ... whatever else ...
To create a global timeout, you can replace call
unittest.main()
with
timeout_decorator.timeout(GLOBAL_TIMEOUT)(unittest.main)()
I built a unittest
timeout solution using context managers (the with
keyowrd), based on this answer.
This approach also uses signal
, so it might only be valid on *nix systems (I’ve only run it in my Ubuntu 16.04 environment).
- Import signal, add a
TestTimeout
exception:
import signal
...
class TestTimeout(Exception):
pass
- Define class
test_timeout
, which will handle the with
blocks:
class test_timeout:
def __init__(self, seconds, error_message=None):
if error_message is None:
error_message = 'test timed out after {}s.'.format(seconds)
self.seconds = seconds
self.error_message = error_message
def handle_timeout(self, signum, frame):
raise TestTimeout(self.error_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0)
- Embed
with test_timeout()
blocks in your unit tests:
def test_foo(self):
with test_timeout(5): # test has 5 seconds to complete
... foo unit test code ...
With this approach, tests that time out will result in an error due to the raise TestTimeout
exception.
Optionally, you could wrap the with test_timeout()
block in a try: except TestTimeout:
block, and handle the exception with more granularity (skip a test instead of error for example).
I’m using python
framework unittest
. Is it possible to specify by framework’s abilities a timeout for test? If no, is it possible to specify gracefully a timeout
for all tests and for some separated tests a private value for each one?
I want to define a global timeout
for all tests (they will use it by default) and a timeout for some test that can take a long time.
As far as I know unittest
does not contain any support for tests timeout.
You can try timeout-decorator
library from PyPI. Apply the decorator on individual tests to make them terminate if they take too long:
import timeout_decorator
class TestCaseWithTimeouts(unittest.TestCase):
# ... whatever ...
@timeout_decorator.timeout(LOCAL_TIMEOUT)
def test_that_can_take_too_long(self):
sleep(float('inf'))
# ... whatever else ...
To create a global timeout, you can replace call
unittest.main()
with
timeout_decorator.timeout(GLOBAL_TIMEOUT)(unittest.main)()
I built a unittest
timeout solution using context managers (the with
keyowrd), based on this answer.
This approach also uses signal
, so it might only be valid on *nix systems (I’ve only run it in my Ubuntu 16.04 environment).
- Import signal, add a
TestTimeout
exception:
import signal
...
class TestTimeout(Exception):
pass
- Define class
test_timeout
, which will handle thewith
blocks:
class test_timeout:
def __init__(self, seconds, error_message=None):
if error_message is None:
error_message = 'test timed out after {}s.'.format(seconds)
self.seconds = seconds
self.error_message = error_message
def handle_timeout(self, signum, frame):
raise TestTimeout(self.error_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0)
- Embed
with test_timeout()
blocks in your unit tests:
def test_foo(self):
with test_timeout(5): # test has 5 seconds to complete
... foo unit test code ...
With this approach, tests that time out will result in an error due to the raise TestTimeout
exception.
Optionally, you could wrap the with test_timeout()
block in a try: except TestTimeout:
block, and handle the exception with more granularity (skip a test instead of error for example).