Поблочное тестирование Django с объектами date/time-based

Если вы хотите отправить данные из одного в другой viewController, вот путь к нему:

Скажем, у нас есть viewControllers: ViewController и NewViewController.

в ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}

@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;

-(IBAction)goToNextScreen:(id)sender;

@end

в ViewController.m

#import "ViewController.h"

#import "NewViewController.h"

@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;

-(IBAction)goToNextScreen:(id)sender
{
    NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];


    NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];

    newVc.arrayList = arr;

    [self.navigationController pushViewController:newVc animated:YES];

}

В NewViewController.h

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

    NSString *name,*age,*dob,*mobile;

}

@property(nonatomic, retain)NSArray *arrayList;

@end

В NewViewController.m

#import "NewViewController.h"

#import "ViewController.h"

@implementation NewViewController
@synthesize arrayList;

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [arrayList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];      
    }
    // Configure the cell...
    cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
    return cell;


}

@end

Таким образом, мы можем передавать данные из одного контроллера представления в другой контроллер представления ...

26
задан Fragsworth 25 June 2009 в 09:35
поделиться

5 ответов

ИЗМЕНИТЬ : Поскольку мой ответ является принятым здесь, я обновляю его, чтобы все знали, что тем временем был создан лучший способ, библиотека freezegun: https: //pypi.python. org / pypi / freezegun . Я использую это во всех своих проектах, когда хочу повлиять на время в тестах. Посмотри на это.

Исходный ответ:

Замена таких внутренних вещей всегда опасна, поскольку может иметь неприятные побочные эффекты. Так что вы действительно хотите, чтобы исправление обезьяны было как можно более локальным.

Мы используем отличную фиктивную библиотеку Майкла Фурда: 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's 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's 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.'))

Надеюсь, это поможет

32
ответ дан 28 November 2019 в 07:12
поделиться

Небольшое изменение решения Стифа. Вместо того, чтобы заменять datetime глобально, вы можете просто заменить модуль datetime только в том модуле, который вы тестируете, например:


import models # your module with the Event model
import datetimestub

models.datetime = datetimestub.DatetimeStub()

Таким образом, изменение будет намного более локализовано во время вашего теста.

6
ответ дан 28 November 2019 в 07:12
поделиться

Это не выполняет общесистемную замену даты и времени, но если вам надоело пытаться заставить что-то работать, вы всегда можете добавить необязательный параметр, чтобы упростить тестирование.

def is_over(self, today=datetime.datetime.now()):
    return today > self.date_end
1
ответ дан 28 November 2019 в 07:12
поделиться

You could write your own datetime module replacement class, implementing the methods and classes from datetime that you want to replace. For example:

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)

Let's put this in its own module we'll call datetimestub.py

Then, at the start of your test, you can do this:

import sys
import datetimestub

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

Any subsequent import of the datetime module will then use the datetimestub.DatetimeStub instance, because when a module's name is used as a key in the sys.modules dictionary, the module will not be imported: the object at sys.modules[module_name] will be used instead.

7
ответ дан 28 November 2019 в 07:12
поделиться

Two choices.

  1. Mock out datetime by providing your own. Since the local directory is searched before the standard library directories, you can put your tests in a directory with your own mock version of datetime. This is harder than it appears, because you don't know all the places datetime is secretly used.

  2. Use Strategy. Replace explicit references to datetime.date.today() and datetime.date.now() in your code with a Factory that generates these. The Factory must be configured with the module by the application (or the unittest). This configuration (called "Dependency Injection" by some) allows you to replace the normal run-time Factory with a special test factory. You gain a lot of flexibility with no special case handling of production. No "if testing do this differently" business.

Here's the Strategy version.

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

    A subclass may apply different rules for determining "today".  
    For example, the broswer's time-zone could be used instead of the
    server's 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

Now you can do this

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 must do this to account for browser locale separate from server locale. Using default datetime.datetime.now() uses the server's locale, which may piss off users who are in a different time zone.

-6
ответ дан 28 November 2019 в 07:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: