summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2013-04-21 22:29:46 +0200
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2013-04-21 22:29:46 +0200
commit3b75e132fd1f302b604e76f4c1a0361c32ba50d4 (patch)
treea285557ceda0d30fe85aa264f6578b1826d0bbb4
parentb5f0579a9050b38392f45eb8e29b51e46928584a (diff)
downloadfactory-boy-3b75e132fd1f302b604e76f4c1a0361c32ba50d4.tar
factory-boy-3b75e132fd1f302b604e76f4c1a0361c32ba50d4.tar.gz
Add tests.alter_time and fix time altering on pypy.
-rw-r--r--tests/alter_time.py113
-rw-r--r--tests/utils.py48
2 files changed, 121 insertions, 40 deletions
diff --git a/tests/alter_time.py b/tests/alter_time.py
new file mode 100644
index 0000000..f426e03
--- /dev/null
+++ b/tests/alter_time.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This code is in the public domain
+# Author: Raphaël Barrois
+
+
+from __future__ import print_function
+
+import datetime
+import mock
+
+
+real_datetime_class = datetime.datetime
+
+def mock_datetime_now(target, datetime_module):
+ """Override ``datetime.datetime.now()`` with a custom target value.
+
+ This creates a new datetime.datetime class, and alters its now()/utcnow()
+ methods.
+
+ Returns:
+ A mock.patch context, can be used as a decorator or in a with.
+ """
+
+ # See http://bugs.python.org/msg68532
+ # And http://docs.python.org/reference/datamodel.html#customizing-instance-and-subclass-checks
+ class DatetimeSubclassMeta(type):
+ """We need to customize the __instancecheck__ method for isinstance().
+
+ This must be performed at a metaclass level.
+ """
+
+ @classmethod
+ def __instancecheck__(mcs, obj):
+ return isinstance(obj, real_datetime_class)
+
+ class BaseMockedDatetime(real_datetime_class):
+ @classmethod
+ def now(cls, tz=None):
+ return target.replace(tzinfo=tz)
+
+ @classmethod
+ def utcnow(cls):
+ return target
+
+ # Python2 & Python3-compatible metaclass
+ MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})
+
+ return mock.patch.object(datetime_module, 'datetime', MockedDatetime)
+
+real_date_class = datetime.date
+
+def mock_date_today(target, datetime_module):
+ """Override ``datetime.date.today()`` with a custom target value.
+
+ This creates a new datetime.date class, and alters its today() method.
+
+ Returns:
+ A mock.patch context, can be used as a decorator or in a with.
+ """
+
+ # See http://bugs.python.org/msg68532
+ # And http://docs.python.org/reference/datamodel.html#customizing-instance-and-subclass-checks
+ class DateSubclassMeta(type):
+ """We need to customize the __instancecheck__ method for isinstance().
+
+ This must be performed at a metaclass level.
+ """
+
+ @classmethod
+ def __instancecheck__(mcs, obj):
+ return isinstance(obj, real_date_class)
+
+ class BaseMockedDate(real_date_class):
+ @classmethod
+ def today(cls):
+ return target
+
+ # Python2 & Python3-compatible metaclass
+ MockedDate = DateSubclassMeta('date', (BaseMockedDate,), {})
+
+ return mock.patch.object(datetime_module, 'date', MockedDate)
+
+
+def main():
+ """Run a couple of tests"""
+ target_dt = real_datetime_class(2009, 1, 1)
+ target_date = real_date_class(2009, 1, 1)
+
+ print("Entering mock")
+ with mock_datetime_now(target_dt, datetime):
+ print("- now ->", datetime.datetime.now())
+ print("- isinstance(now, dt) ->", isinstance(datetime.datetime.now(), datetime.datetime))
+ print("- isinstance(target, dt) ->", isinstance(target_dt, datetime.datetime))
+
+ with mock_date_today(target_date, datetime):
+ print("- today ->", datetime.date.today())
+ print("- isinstance(now, date) ->", isinstance(datetime.date.today(), datetime.date))
+ print("- isinstance(target, date) ->", isinstance(target_date, datetime.date))
+
+
+ print("Outside mock")
+ print("- now ->", datetime.datetime.now())
+ print("- isinstance(now, dt) ->", isinstance(datetime.datetime.now(), datetime.datetime))
+ print("- isinstance(target, dt) ->", isinstance(target_dt, datetime.datetime))
+
+ print("- today ->", datetime.date.today())
+ print("- isinstance(now, date) ->", isinstance(datetime.date.today(), datetime.date))
+ print("- isinstance(target, date) ->", isinstance(target_date, datetime.date))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/utils.py b/tests/utils.py
index 823e82b..0111df3 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -23,50 +23,23 @@
import datetime
from .compat import mock
+from . import alter_time
class MultiModulePatcher(object):
"""An abstract context processor for patching multiple modules."""
- replaced_symbol = None
- replaced_symbol_module = None
- module_imported_as = ''
-
def __init__(self, *target_modules, **kwargs):
super(MultiModulePatcher, self).__init__(**kwargs)
- if not self.module_imported_as:
- self.module_imported_as = replaced_symbol.__module__.__name__
self.patchers = [self._build_patcher(mod) for mod in target_modules]
- def _check_module(self, target_module):
- if not self.replaced_symbol_module:
- # No check to perform
- return
-
- replaced_import = getattr(target_module, self.module_imported_as)
- assert replaced_import is self.replaced_symbol_module, (
- "Cannot patch import %s.%s (%r != %r)" % (
- target_module.__name__, self.module_imported_as,
- replaced_import, self.replaced_symbol_module))
-
def _build_patcher(self, target_module):
"""Build a mock patcher for the target module."""
- self._check_module(target_module)
-
- return mock.patch.object(
- getattr(target_module, self.module_imported_as),
- self.replaced_symbol.__name__,
- mock.Mock(wraps=self.replaced_symbol),
- )
-
- def setup_mocked_symbol(self, mocked_symbol):
- """Setup a mocked symbol for later use."""
- pass
+ raise NotImplementedError()
def __enter__(self):
for patcher in self.patchers:
mocked_symbol = patcher.start()
- self.setup_mocked_symbol(mocked_symbol)
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
for patcher in self.patchers:
@@ -75,26 +48,21 @@ class MultiModulePatcher(object):
class mocked_date_today(MultiModulePatcher):
"""A context processor changing the value of date.today()."""
- replaced_symbol = datetime.date
- replaced_symbol_module = datetime
- module_imported_as = 'datetime'
def __init__(self, target_date, *target_modules, **kwargs):
self.target_date = target_date
super(mocked_date_today, self).__init__(*target_modules, **kwargs)
- def setup_mocked_symbol(self, mocked_date):
- mocked_date.today.return_value = self.target_date
+ def _build_patcher(self, target_module):
+ module_datetime = getattr(target_module, 'datetime')
+ return alter_time.mock_date_today(self.target_date, module_datetime)
class mocked_datetime_now(MultiModulePatcher):
- replaced_symbol = datetime.datetime
- replaced_symbol_module = datetime
- module_imported_as = 'datetime'
-
def __init__(self, target_dt, *target_modules, **kwargs):
self.target_dt = target_dt
super(mocked_datetime_now, self).__init__(*target_modules, **kwargs)
- def setup_mocked_symbol(self, mocked_datetime):
- mocked_datetime.now.return_value = self.target_dt
+ def _build_patcher(self, target_module):
+ module_datetime = getattr(target_module, 'datetime')
+ return alter_time.mock_datetime_now(self.target_dt, module_datetime)