How to stub time.sleep() in Python unit testing

Question:

I want to make a stub to prevent time.sleep(..) to sleep to improve the unit test execution time.

What I have is:

import time as orgtime

class time(orgtime):
    '''Stub for time.'''
    _sleep_speed_factor = 1.0

    @staticmethod
    def _set_sleep_speed_factor(sleep_speed_factor):
        '''Sets sleep speed.'''
        time._sleep_speed_factor = sleep_speed_factor


    @staticmethod
    def sleep(duration):
        '''Sleeps or not.'''
        print duration * time._sleep_speed_factor
        super.sleep(duration * time._sleep_speed_factor) 

However, I get the following error on the second code line above (class definition):

TypeError: Error when calling the metaclass bases
module.__init__() takes at most 2 arguments (3 given).

How to fix the error?

Asked By: Michel Keijzers

||

Answers:

You can use mock library in your tests.

import time
from mock import patch

class MyTestCase(...):


     @patch('time.sleep', return_value=None)
     def my_test(self, patched_time_sleep):
          time.sleep(666)  # Should be instant
Answered By: Mikko Ohtamaa

What about:

import time
from time import sleep as originalsleep

def newsleep(seconds):
    sleep_speed_factor = 10.0 
    originalsleep(seconds/sleep_speed_factor)

time.sleep = newsleep

This is working for me. I am inlcuding it at the beginning of the test I want to speed up, at the end I set back the original sleep just in case. Hope it helps

Answered By: sarusso

using freezegun package can help you to do this.

# fake.py
import functools
from datetime import datetime, timedelta
from unittest import mock

from freezegun import freeze_time


def fake_sleep(func):
    freezegun_control = None

    def fake_sleep(seconds):
        nonlocal freezegun_control
        utcnow = datetime.utcnow()
        if freezegun_control is not None:
            freezegun_control.stop()
        freezegun_control = freeze_time(utcnow + timedelta(seconds=seconds))
        freezegun_control.start()

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        with mock.patch('time.sleep', fake_sleep):
            rv = func(*args, **kwargs)

            if freezegun_control is not None:
                freezegun_control.stop()
            return rv

    return wrapper


# test.py
from fake import fake_sleep

import time

@fake_sleep
def test_sleep():
    now = datetime.utcnow()

    for sleep_seconds in range(10):
        for i in range(1, 10):

            time.sleep(sleep_seconds)

            assert datetime.utcnow() - now >= timedelta(
                seconds=i * sleep_seconds)
  1. common demo: please see the freezegun README
  2. pytest demo: Gist fake sleep function fixture
Answered By: linw1995

The accepted answer is still valid. However, unittest.mock is since Python 3.3 an official part of the Python standard library.

import time
from unittest import TestCase
from unittest.mock import patch

class TestMyCase(TestCase):

    @patch('time.sleep', return_value=None)
    def test_my_method(self, patched_time_sleep):
        time.sleep(60)  # Should be instant

        # the mock should only be called once
        self.assertEqual(1, patched_time_sleep.call_count)
        # or 
        patched_time_sleep.assert_called_once()

    # alternative version using a context manager
    def test_my_method_alternative(self):
        with patch('time.sleep', return_value=None) as patched_time_sleep:
            time.sleep(60)  # Should be instant

        # the mock should only be called once
        self.assertEqual(1, patched_time_sleep.call_count)
        # or 
        patched_time_sleep.assert_called_once()
Answered By: Jeeppler

I’m using pytest and have following fixture to monkey patch time.sleep:

import pytest


@pytest.fixture
def sleepless(monkeypatch):

    def sleep(seconds):
        pass

    monkeypatch.setattr(time, 'sleep', sleep)

Then in test which I need to “speedup” the sleep, I just use this fixture:

import time

def test_sleep(sleepless):
    time.sleep(60)

So when you run this test, you will see that it completes in much shorter time:

= 1 passed in 0.02 seconds =
Answered By: sashk

Here’s what I did to prevent the test from sleeping:

If I have a module mymodule.py that imports and uses sleep in a function that I want to test:

from time import sleep

def some_func()
    sleep(5)
    # ...do some things

I then have my test import sleep from the module that is using it, like this:

@mock.patch('mymodule.sleep')
def test_some_func(mock_sleep):
    mock_sleep.return_value = None
    # ...continue my test
Answered By: CodeBiker