summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/fuzzy.rst27
-rw-r--r--factory/fuzzy.py20
-rw-r--r--tests/test_fuzzy.py61
3 files changed, 108 insertions, 0 deletions
diff --git a/docs/fuzzy.rst b/docs/fuzzy.rst
index f1f4085..7d56995 100644
--- a/docs/fuzzy.rst
+++ b/docs/fuzzy.rst
@@ -70,6 +70,33 @@ FuzzyInteger
int, the inclusive higher bound of generated integers
+FuzzyDate
+---------
+
+.. class:: FuzzyDate(start_date[, end_date])
+
+ The :class:`FuzzyDate` fuzzer generates random dates within a given
+ inclusive range.
+
+ The :attr:`end_date` bound may be omitted, in which case it defaults to the current date:
+
+ .. code-block:: pycon
+
+ >>> fd = FuzzyDate(datetime.date(2008, 1, 1))
+ >>> fd.start_date, fd.end_date
+ datetime.date(2008, 1, 1), datetime.date(2013, 4, 16)
+
+ .. attribute:: start_date
+
+ :class:`datetime.date`, the inclusive lower bound of generated dates
+
+ .. attribute:: end_date
+
+ :class:`datetime.date`, the inclusive higher bound of generated dates
+
+ int, the inclusive higher bound of generated dates
+
+
Custom fuzzy fields
-------------------
diff --git a/factory/fuzzy.py b/factory/fuzzy.py
index 186b4a7..fea7b05 100644
--- a/factory/fuzzy.py
+++ b/factory/fuzzy.py
@@ -25,6 +25,7 @@
import random
+import datetime
from . import declarations
@@ -84,3 +85,22 @@ class FuzzyInteger(BaseFuzzyAttribute):
def fuzz(self):
return random.randint(self.low, self.high)
+
+
+class FuzzyDate(BaseFuzzyAttribute):
+ """Random date within a given date range."""
+ def __init__(self, start_date, end_date=None, **kwargs):
+ super(FuzzyDate, self).__init__(**kwargs)
+ if end_date is None:
+ end_date = datetime.date.today()
+
+ if start_date > end_date:
+ raise ValueError(
+ "FuzzyDate boundaries should have start <= end; got %r > %r."
+ % (start_date, end_date))
+
+ self.start_date = start_date.toordinal()
+ self.end_date = end_date.toordinal()
+
+ def fuzz(self):
+ return datetime.date.fromordinal(random.randint(self.start_date, self.end_date))
diff --git a/tests/test_fuzzy.py b/tests/test_fuzzy.py
index 70a2095..e3b5772 100644
--- a/tests/test_fuzzy.py
+++ b/tests/test_fuzzy.py
@@ -21,6 +21,8 @@
# THE SOFTWARE.
+import datetime
+
from factory import fuzzy
from .compat import mock, unittest
@@ -102,3 +104,62 @@ class FuzzyIntegerTestCase(unittest.TestCase):
res = fuzz.evaluate(2, None, False)
self.assertEqual(8, res)
+
+
+class FuzzyDateTestCase(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # Setup useful constants
+ cls.jan1 = datetime.date(2013, 1, 1)
+ cls.jan3 = datetime.date(2013, 1, 3)
+ cls.jan31 = datetime.date(2013, 1, 31)
+
+ def test_accurate_definition(self):
+ """Tests all ways of defining a FuzzyDate."""
+ fuzz = fuzzy.FuzzyDate(self.jan1, self.jan31)
+
+ for _i in range(20):
+ res = fuzz.evaluate(2, None, False)
+ self.assertLessEqual(self.jan1, res)
+ self.assertLessEqual(res, self.jan31)
+
+ def test_partial_definition(self):
+ """Test defining a FuzzyDate without passing an end date."""
+ with utils.mocked_date_today(self.jan3, fuzzy):
+ fuzz = fuzzy.FuzzyDate(self.jan1)
+
+ for _i in range(20):
+ res = fuzz.evaluate(2, None, False)
+ self.assertLessEqual(self.jan1, res)
+ self.assertLessEqual(res, self.jan3)
+
+ def test_invalid_definition(self):
+ self.assertRaises(ValueError, fuzzy.FuzzyDate,
+ self.jan31, self.jan1)
+
+ def test_invalid_partial_definition(self):
+ with utils.mocked_date_today(self.jan1, fuzzy):
+ self.assertRaises(ValueError, fuzzy.FuzzyDate,
+ self.jan31)
+
+ def test_biased(self):
+ """Tests a FuzzyDate with a biased random.randint."""
+
+ fake_randint = lambda low, high: (low + high) // 2
+ fuzz = fuzzy.FuzzyDate(self.jan1, self.jan31)
+
+ with mock.patch('random.randint', fake_randint):
+ res = fuzz.evaluate(2, None, False)
+
+ self.assertEqual(datetime.date(2013, 1, 16), res)
+
+ def test_biased_partial(self):
+ """Tests a FuzzyDate with a biased random and implicit upper bound."""
+ with utils.mocked_date_today(self.jan3, fuzzy):
+ fuzz = fuzzy.FuzzyDate(self.jan1)
+
+ fake_randint = lambda low, high: (low + high) // 2
+ with mock.patch('random.randint', fake_randint):
+ res = fuzz.evaluate(2, None, False)
+
+ self.assertEqual(datetime.date(2013, 1, 2), res)