From 91202a636511bf7e6384f90105e512f3f1aa98d7 Mon Sep 17 00:00:00 2001 From: Peter Marsh Date: Fri, 16 Jun 2017 10:50:52 +0100 Subject: [PATCH] Fixed #16 - Incorrect test duration with freezegun freezegun is a Python library used to set the date/time returned by the Python date/time functions to make testing time-sensitive code easier. time.time() is used here to measure the duration of test cases, if freezegun was in use then the time returned by this could be the time the test had set up, rather than the actual time. This lead to test durations being reported as negative (if frozen time is in the past), or much longer than expected (if fronzen time is in the future). freezegun exposes the unmodified time.time() via an alias. This changes TimingSuite so that it uses this alias if freezegun is installed. --- .travis.yml | 2 +- django_slowtests/testrunner.py | 14 ++++++++++++-- django_slowtests/tests/fake.py | 15 +++++++++++++++ django_slowtests/tests/tests.py | 25 ++++++++++++++++++++++++- tox.ini | 1 + 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1fa716b..067f536 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ env: - DJANGO=Django==1.6.11 install: - - pip install flake8 coverage coveralls $DJANGO + - pip install flake8 coverage coveralls $DJANGO freezegun - pip install -e . matrix: diff --git a/django_slowtests/testrunner.py b/django_slowtests/testrunner.py index 23ff3e7..b7d1180 100644 --- a/django_slowtests/testrunner.py +++ b/django_slowtests/testrunner.py @@ -7,6 +7,16 @@ from django.conf import settings +try: # pragma: no cover + import freezegun + + def _time(): + return freezegun.api.real_time() +except ImportError: # pragma: no cover + def _time(): + return time.time() + + TIMINGS = {} NUM_SLOW_TESTS = getattr(settings, 'NUM_SLOW_TESTS', 10) SLOW_TEST_THRESHOLD_MS = getattr(settings, 'SLOW_TEST_THRESHOLD_MS', 0) @@ -26,7 +36,7 @@ def run(self, result, debug=False): if result.shouldStop: break - start_time = time.time() + start_time = _time() if _isnotsuite(test): self._tearDownPreviousClass(test, result) @@ -43,7 +53,7 @@ def run(self, result, debug=False): else: test.debug() - TIMINGS[str(test)] = time.time() - start_time + TIMINGS[str(test)] = _time() - start_time if topLevel: self._tearDownPreviousClass(None, result) diff --git a/django_slowtests/tests/fake.py b/django_slowtests/tests/fake.py index 323c5cd..4b591e5 100644 --- a/django_slowtests/tests/fake.py +++ b/django_slowtests/tests/fake.py @@ -1,5 +1,6 @@ import time from django.test import TestCase +from freezegun import freeze_time class FakeTestCase(TestCase): @@ -15,3 +16,17 @@ def test_slow_thing(self): def test_setup_class_was_run(self): self.assertTrue(self._setupClassRan) + + +@freeze_time('2016-02-03 12:34:56') +class FakeFrozenInPastTestCase(TestCase): + + def test_this_should_not_have_a_negative_duration(self): + self.assertTrue(True) + + +@freeze_time('3017-02-03 12:34:56') +class FakeFrozenInFutureTestCase(TestCase): + + def test_this_should_not_have_very_long_duration(self): + self.assertTrue(True) diff --git a/django_slowtests/tests/tests.py b/django_slowtests/tests/tests.py index c2ac14d..3aa8dae 100644 --- a/django_slowtests/tests/tests.py +++ b/django_slowtests/tests/tests.py @@ -1,10 +1,13 @@ from django.test import TestCase from unittest import TestResult -from ..testrunner import TimingSuite +from ..testrunner import TimingSuite, TIMINGS class TimingSuiteTests(TestCase): + def setUp(self): + TIMINGS.clear() + def test_add_a_test(self): from .fake import FakeTestCase suite = TimingSuite() @@ -14,3 +17,23 @@ def test_add_a_test(self): suite.run(result) self.assertEquals(len(suite._tests), 2) self.assertEquals(len(result.errors), 0) + + def test_timing_is_correct_when_freezegun_sets_time_in_past(self): + from .fake import FakeFrozenInPastTestCase + suite = TimingSuite() + result = TestResult() + suite.addTest(FakeFrozenInPastTestCase('test_this_should_not_have_a_negative_duration')) + suite.run(result) + test_name = str(suite._tests[0]) + self.assertTrue(TIMINGS[test_name] > 0) + self.assertTrue(TIMINGS[test_name] < 1) + + def test_timing_is_correct_when_freezegun_sets_time_in_future(self): + from .fake import FakeFrozenInFutureTestCase + suite = TimingSuite() + result = TestResult() + suite.addTest(FakeFrozenInFutureTestCase('test_this_should_not_have_very_long_duration')) + suite.run(result) + test_name = str(suite._tests[0]) + self.assertTrue(TIMINGS[test_name] > 0) + self.assertTrue(TIMINGS[test_name] < 1) diff --git a/tox.ini b/tox.ini index 8046843..e3924f3 100644 --- a/tox.ini +++ b/tox.ini @@ -9,4 +9,5 @@ deps = dj1.8: Django>=1.8,<1.9 dj1.9: Django>=1.9,<1.10 dj1.10: Django>=1.10,<1.11 + freezegun>=0.1.8 commands = coverage run setup.py test