Test unitario in Django

12

Sto davvero lottando per scrivere test unitari efficaci per un grande progetto Django. Ho una copertura di prova ragionevolmente buona, ma ho capito che i test che ho scritto sono senz'altro test di integrazione / accettazione, non test unitari, e ho parti critiche della mia applicazione che non sono state testate in modo efficace. Voglio risolvere questo APPENA POSSIBILE.

Ecco il mio problema. Il mio schema è profondamente relazionale e strongmente orientato al tempo, dando al mio oggetto modello un elevato accoppiamento interno e molto stato. Molti dei miei metodi di modello interrogano in base a intervalli di tempo e ho un sacco di auto_now_add in corso in campi con data e ora. Quindi prendi un metodo come questo, ad esempio:

def summary(self, startTime=None, endTime=None):
    # ... logic to assign a proper start and end time 
    # if none was provided, probably using datetime.now()

    objects = self.related_model_set.manager_method.filter(...)

    return sum(object.key_method(startTime, endTime) for object in objects)

Come ci si avvicina a testare qualcosa del genere?

Ecco dove sono fin qui. Mi viene in mente che l'obiettivo di test dell'unità dovrebbe essere dato un comportamento simulato by key_method sui suoi argomenti, è summary correttamente filtraggio / aggregazione per produrre un risultato corretto?

Mocking datetime.now () è abbastanza semplice, ma come posso prendere in giro il resto del comportamento?

  • Potrei usare le fixture, ma ho sentito pro e contro usare le fixture per costruire i miei dati (la scarsa manutenibilità è una truffa che mi colpisce per casa).
  • Potrei anche configurare i miei dati attraverso l'ORM, ma ciò può essere limitante, perché poi devo creare anche oggetti correlati. E l'ORM non ti consente di fare confusione con i campi di auto_now_add manualmente.
  • Deridere l'ORM è un'altra opzione, ma non è solo complicato prendere in giro i metodi ORM profondamente annidati, ma la logica nel codice ORM viene derisa dal test e il mocking sembra rendere il test realmente dipendente dagli interni e dipendenze della funzione sotto test.

I dadi più difficili da decifrare sembrano essere le funzioni come questa, che siedono su pochi livelli di modelli e funzioni di livello inferiore e dipendono molto dal tempo, anche se queste funzioni potrebbero non essere super complicate. Il mio problema generale è che, a prescindere da quanto sembri affettare, i miei test sembrano molto più complessi delle funzioni che stanno testando.

    
posta acjay 04.03.2013 - 06:50
fonte

1 risposta

6

Ho intenzione di andare avanti e registrare una risposta per quello che ho inventato finora.

La mia ipotesi è che per una funzione con accoppiamento e stato profondi, la realtà è che semplicemente prenderà un sacco di linee da controllare per il suo contesto esterno.

Ecco come appare il mio caso di test, basandosi sulla libreria Mock standard:

  1. Uso l'ORM standard per impostare la sequenza di eventi
  2. Creo il mio inizio datetime e sovverto auto_now_add volte per adattare una timeline fissa del mio progetto. Ho pensato che l'ORM non lo permettesse, ma funziona bene.
  3. Mi assicuro che la funzione sotto test utilizzi from datetime import datetime in modo che possa applicare datetime.now() in questa funzione (se faccio il mocking dell'intera classe datetime , l'ORM esegue un adattamento).
  4. Creo la mia sostituzione per object.key_method() , con funzionalità semplici ma ben definite che dipendono dagli argomenti. Voglio che dipenda dagli argomenti, perché altrimenti potrei non sapere se la logica della funzione sotto test funziona. Nel mio caso, restituisce semplicemente il numero di secondi tra startTime e endTime . Lo aggiusto inserendolo in una lambda e applicando le patch direttamente su object.key_method() utilizzando new_callable kwarg di patch .
  5. Infine, eseguo una serie di asserzioni su vari richiami di summary con diversi argomenti per controllare l'uguaglianza con i risultati calcolati a mano attesi per il dato comportamento del simulato key_method

Inutile dire che questo è significativamente più lungo e più complicato della funzione stessa. Dipende dal DB e non sembra davvero un test unitario. Ma è anche abbastanza disaccoppiato dagli interni della funzione, solo dalla sua firma e dalle sue dipendenze. Quindi penso che potrebbe effettivamente essere un test unitario, ancora.

Nella mia app, la funzione è piuttosto cruciale e soggetta al refactoring per ottimizzare le sue prestazioni. Quindi penso che il problema ne valga la pena, nonostante la complessità. Ma sono ancora aperto a idee migliori su come affrontarlo. Tutto parte del mio lungo viaggio verso uno stile di sviluppo più guidato dai test ...

    
risposta data 04.03.2013 - 22:24
fonte

Leggi altre domande sui tag