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.

Asked By: Jury

||

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)()
Answered By: Lav

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).

  1. Import signal, add a TestTimeout exception:
import signal

...

class TestTimeout(Exception):
    pass
  1. 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)
  1. 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).

Answered By: caffreyd
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.