Django group testing with date / time based objects

Suppose I have the following model Event:

from django.db import models
import datetime

class Event(models.Model):
    date_start = models.DateField()
    date_end = models.DateField()

    def is_over(self):
        return datetime.date.today() > self.date_end

I want to test Event.is_over()by creating an Event that ends in the future (today + 1 or something), and completing the date and time so that the system thinks that we have reached this future date.

I would like to be able to stub ALL system time objects before python. This includes datetime.date.today(), datetime.datetime.now()and any other standard date / time objects.

What is the standard way to do this?

+23
source share
7 answers

EDIT. , , , , freezegun: https://pypi.python.org/pypi/freezegun. , . .

:

, , , . , .

mock- Michael Foord: http://www.voidspace.org.uk/python/mock/, @patch, , , , .

, datetime C, . , , .

- - ( - , Django, , ). , , , , , , :)

datetime.date.today utils/date.py:

import datetime

def today():
    return datetime.date.today()

unittest tests.py:

import datetime
import mock
from unittest2 import TestCase

from django.core.exceptions import ValidationError

from .. import validators

class ValidationTests(TestCase):
    @mock.patch('utils.date.today')
    def test_validate_future_date(self, today_mock):
        # Pin python today to returning the same date
        # always so we can actually keep on unit testing in the future :)
        today_mock.return_value = datetime.date(2010, 1, 1)

        # A future date should work
        validators.validate_future_date(datetime.date(2010, 1, 2))

        # The mocked today date should fail
        with self.assertRaises(ValidationError) as e:
            validators.validate_future_date(datetime.date(2010, 1, 1))
        self.assertEquals([u'Date should be in the future.'], e.exception.messages)

        # Date in the past should also fail
        with self.assertRaises(ValidationError) as e:
            validators.validate_future_date(datetime.date(2009, 12, 31))
        self.assertEquals([u'Date should be in the future.'], e.exception.messages)

:

from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError

from utils import date

def validate_future_date(value):
    if value <= date.today():
        raise ValidationError(_('Date should be in the future.'))

,

+29

, , . :

import datetime as datetime_orig

class DatetimeStub(object):
    """A datetimestub object to replace methods and classes from 
    the datetime module. 

    Usage:
        import sys
        sys.modules['datetime'] = DatetimeStub()
    """
    class datetime(datetime_orig.datetime):

        @classmethod
        def now(cls):
            """Override the datetime.now() method to return a
            datetime one year in the future
            """
            result = datetime_orig.datetime.now()
            return result.replace(year=result.year + 1)

    def __getattr__(self, attr):
        """Get the default implementation for the classes and methods
        from datetime that are not replaced
        """
        return getattr(datetime_orig, attr)

, datetimestub.py

, , :

import sys
import datetimestub

sys.modules['datetime'] = datetimestub.DatetimeStub()

datetime datetimestub.DatetimeStub, , sys.modules, : sys.modules[module_name] .

+7

Steef. datetime datetime , , :


import models # your module with the Event model
import datetimestub

models.datetime = datetimestub.DatetimeStub()

, .

+6

, self.end_date datetime? , , , .

/, , .

today = datetime.date.today()

event1 = Event()
event1.end_date = today - datetime.timedelta(days=1) # 1 day ago
event2 = Event()
event2.end_date = today + datetime.timedelta(days=1) # 1 day in future

self.assertTrue(event1.is_over())
self.assertFalse(event2.is_over())
+3

-, - , , .

def is_over(self, today=datetime.datetime.now()):
    return today > self.date_end
+1

.

  • , . , datetime. , , , .

  • . datetime.date.today() datetime.date.now() Factory, . Factory ( unittest). ( "Injection Dependency" ) Factory factory. . " -".

.

class DateTimeFactory( object ):
    """Today and now, based on server defined locale.

    A subclass may apply different rules for determining "today".  
    For example, the broswer time-zone could be used instead of the
    server timezone.
    """
    def getToday( self ):
        return datetime.date.today()
    def getNow( self ):
        return datetime.datetime.now()

class Event( models.Model ):
    dateFactory= DateTimeFactory() # Definitions of "now" and "today".
    ... etc. ...

    def is_over( self ):
        return dateFactory.getToday() > self.date_end 


class DateTimeMock( object ):
    def __init__( self, year, month, day, hour=0, minute=0, second=0, date=None ):
        if date:
            self.today= date
            self.now= datetime.datetime.combine(date,datetime.time(hour,minute,second))
        else:
            self.today= datetime.date(year, month, day )
            self.now= datetime.datetime( year, month, day, hour, minute, second )
    def getToday( self ):
        return self.today
    def getNow( self ):
        return self.now

class SomeTest( unittest.TestCase ):
    def setUp( self ):
        tomorrow = datetime.date.today() + datetime.timedelta(1)
        self.dateFactoryTomorrow= DateTimeMock( date=tomorrow )
        yesterday = datetime.date.today() + datetime.timedelta(1)
        self.dateFactoryYesterday=  DateTimeMock( date=yesterday )
    def testThis( self ):
        x= Event( ... )
        x.dateFactory= self.dateFactoryTomorrow
        self.assertFalse( x.is_over() )
        x.dateFactory= self.dateFactoryYesterday
        self.asserTrue( x.is_over() )

In the long run you more or less have to do is to take into account the locale browser separately from the server locale. Usage by default datetime.datetime.now()uses the server locale, which can call users who are in a different time zone.

-5
source

Source: https://habr.com/ru/post/1541937/


All Articles