Come mock python datetime.now() in un metodo di una classe per i test di unità?

Sto cercando di scrivere test per una classe che dispone di metodi come:

import datetime
import pytz

class MyClass:
    def get_now(self, timezone):
        return datetime.datetime.now(timezone)

    def do_many_things(self, tz_string='Europe/London'):
        tz = pytz.timezone(tz_string)
        localtime_now = self.get_now(tz)
        ...
        return things

Voglio provarla, e per farlo ho bisogno di assicurarsi che il datetime.datetime.now() chiamata restituisce qualcosa di prevedibile.

Ho letto un sacco di esempi di utilizzo di Finto nei test, ma non ho trovato niente di abbastanza come quello di cui ho bisogno, e non riesco a capire come usarlo nel mio test.

Ho separato il get_now() metodo nel caso in cui è più facile finto che, invece di datetime.datetime.now(), ma io sono ancora perplesso. Ogni pensiero su come scrivere UnitTests per questo utilizzo Mock? (Questo è tutto in Django, fwiw, io non sono sicuro se questo fa la differenza in questo caso.)

  • Cordiali saluti, non usare mai un pytz timezone, il costruttore datetime. Utilizzare localize invece.
  • Grazie Marco. Così, invece di datetime.datetime.now(timezone) devo fare timezone.localize(datetime.datetime.now())? Qualche particolare ragione è meglio?
  • A volte assegnazione di fuso orario, direttamente non funziona correttamente. Per un esempio, vedere stackoverflow.com/questions/12808845/…
  • Anche se, localize() viene utilizzato solo per ingenuo datetimes. Quindi, se now() è 2012-10-26 15:00:00 (senza fuso orario), allora semplicemente si applica specificata fuso orario; è non convertire il tempo. Come vorrei avere il tempo effettivo in un fuso orario, penso che ho bisogno di fare: datetime.datetime.now(pytz.utc).astimezone(timezone).

 

5 Replies
  1. 23

    Devi creare una funzione che restituisce una specifica data e ora, localizzate per il fuso orario passati:

    import mock
    
    def mocked_get_now(timezone):
        dt = datetime.datetime(2012, 1, 1, 10, 10, 10)
        return timezone.localize(dt)
    
    @mock.patch('path.to.your.models.MyClass.get_now', side_effect=mocked_get_now)
    def your_test(self, mock_obj):
        # Within this test, `MyClass.get_now()` is a mock that'll return a predictable
        # timezone-aware datetime object, set to 2012-01-01 10:10:10.

    Che modo è possibile verificare se la risultante timezone-conoscenza datetime viene correttamente gestita; risultati altrove dovrebbe mostrare il fuso orario corretto, ma avrà una prevedibile data e l’ora.

    Si utilizza il mocked_get_now funzione come un effetto collaterale quando beffardo get_now; ogni volta che il codice chiama get_now chiamata è registrato da mock, e mocked_get_now è chiamato, ed è utilizzato come valore di ritorno il valore restituito al chiamante di get_now.

    • non in grado di importare mock
    • è un pacchetto add-on, da installare con il pip. Con Python 3 è incluso come unittest.mock.
  2. 35

    Si potrebbe utilizzare freezegun :

    from freezegun import freeze_time
    
    def test():
        assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
        with freeze_time("2012-01-14"):
            assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
        assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)

    Sostanzialmente prende in giro datetime modulo di chiamate.

    • Esattamente quello che mi serviva. Grazie per sugegsting
    • Grazie, funziona! Tuttavia, la prima affermazione non perché il mese di token non è stato accettato in questo formato: 01 ma datetime.datetime(2012, 1, 14) opere.
    • Questo è molto pulito risposta. Grazie per il suggerimento.
    • freezegun è lento, soprattutto se si prova logica con più chiamate a datetime.now()
  3. 4

    Sto usando date, ma la stessa idea dovrebbe funzionare per datetime:

    class SpoofDate(date):
        def __new__(cls, *args, **kwargs):
            return date.__new__(date, *args, **kwargs)

    from mock import patch
    
    @patch('some.module.date', SpoofDate)
    def testSomething(self):
        SpoofDate.today = classmethod(lambda cls : date(2012, 9, 24))

    Dove some.module importazioni date. La Patch è di sostituire importati date con SpoofDate, che è quindi possibile ridefinire per fare quello che vuoi.

    • Mi ha aiutato! Grazie!
    • hai semplicemente sostituire date con datetime? Si può scrivere l’attuazione in datetime.
    • qui di seguito è un esempio di quello che ho fatto: classe StubDate(datetime.datetime): pass @mock.patch(“amico.datetime.datetime”, StubDate) def test_generate_date(self): # Make datetime.datetime.ora restituire un valore fisso StubDate.ora = classmethod(lambda cls: tipo datetime.datetime(2015, 03, 11, 11, 01)) auto.assertEqual( self.friend_obj.generate_date(input), di tipo datetime.datetime(2015, 03, 11, 11, 01)) > Blockquote
    • grazie mille! Ho scritto il gist qui per una migliore leggibilità. gist.github.com/za/2a217c47582737f88259
  4. 0

    Utilizzando patch di unittest.mock

    from unittest.mock import patch
    
    @patch('MyClass.datetime')
    def test_foo(self, mock_datetime):
        mock_datetime.datetime.now.return_value = datetime.datetime(2019, 5, 7) #SOME_MOCKED_DATE

    Nota che siamo override datetime modulo che viene importato solo nella nostra classe

    Classe per la quale stiamo scrivendo il test:

    import datetime
    
    class MyClass:
        def foo():
           localtime_now = datetime.datetime.now(timezone)

    Non abbiamo bisogno di separare come get_now() metodo per rendere più facile per deridere.

Lascia un commento