È una buona idea misurare le prestazioni di un metodo utilizzando il timeout del test unitario?


14

In un progetto in cui vi sono requisiti non funzionali che specificano il tempo massimo di esecuzione per un'azione specifica, il QA deve verificare le prestazioni di questa azione su una macchina dedicata utilizzando hardware preciso con carico preciso, sia l'hardware che il carico sono specificati nei requisiti.

D'altra parte, alcune modifiche errate al codice sorgente possono influire negativamente sulle prestazioni. Notare presto questo impatto negativo , prima che il codice sorgente raggiunga il controllo del codice sorgente e sia verificato dal dipartimento di controllo qualità, potrebbe essere utile in termini di tempo perso dal dipartimento di controllo qualità che segnala il problema e dallo sviluppatore che lo risolve più volte dopo.

Per fare questo, è una buona idea:

  • Per utilizzare i test unitari per avere un'idea del tempo impiegato per eseguire la stessa azione² n volte,

  • Per utilizzare il timeout per test tramite l' [TestMethod, Timeout(200)]attributo in C #?

Mi aspetto diversi problemi con questo approccio:

  • Concettualmente , i test unitari non sono proprio per questo: si prevede che testino una piccola parte di un codice, niente di più: né il controllo di un requisito funzionale, né un test di integrazione, né un test delle prestazioni.

  • Il timeout del test unitario in Visual Studio misura davvero ciò che ci si aspetta venga misurato, tenendo conto del fatto che l'inizializzazione e la pulizia sono inesistenti per tali test o sono troppo brevi per influire sui risultati?

  • Misurare le prestazioni in questo modo è brutto. Eseguire un benchmark su qualsiasi macchina¹ indipendentemente dall'hardware, dal carico, ecc. È come fare un benchmark che mostri che un prodotto di database è sempre più veloce di un altro. D'altra parte, non mi aspetto che i test unitari siano un risultato definitivo, né qualcosa che viene utilizzato dal dipartimento di controllo qualità . Tali unit test verranno utilizzati solo per dare un'idea generale delle prestazioni previste e essenzialmente per avvisare lo sviluppatore che la sua ultima modifica ha rotto qualcosa, compromettendo gravemente le prestazioni .

  • Test Driven Development (TDD) è impossibile per questi test. Come avrebbe fallito, in primo luogo, prima di iniziare a implementare il codice?

  • Troppi test delle prestazioni influenzeranno il tempo necessario per eseguire i test, quindi questo approccio è limitato solo alle azioni brevi.

Tenendo conto di questi problemi, trovo ancora interessante utilizzare tali test unitari se combinati con le metriche delle prestazioni reali del dipartimento di controllo qualità.

Ho sbagliato? Ci sono altri problemi che rendono totalmente inaccettabile l'uso di unit test per questo?

In caso di errore, qual è il modo corretto per avvisare lo sviluppatore che una modifica del codice sorgente ha influito gravemente sulle prestazioni, prima che il codice sorgente raggiunga il controllo del codice sorgente e sia verificato dal dipartimento QA?


¹ In realtà, i test unitari dovrebbero essere eseguiti solo su PC sviluppatori con prestazioni hardware comparabili, il che riduce il divario tra le macchine più veloci che non saranno mai in grado di fallire il test delle prestazioni e le macchine più lente che non riusciranno mai a superarlo.

² Per azione intendo un pezzo di codice piuttosto breve che impiega alcuni millisecondi per essere eseguito.

Risposte:


3

Stiamo utilizzando anche questo approccio, ovvero abbiamo test che misurano il runtime in uno scenario di carico definito su una determinata macchina. Potrebbe essere importante sottolineare che non li includiamo nei normali test unitari. I test unitari vengono sostanzialmente eseguiti da ogni sviluppatore su una macchina sviluppatore prima di eseguire le modifiche. Vedi sotto per questo non ha alcun senso per i test delle prestazioni (almeno nel nostro caso). Eseguiamo invece test delle prestazioni come parte dei test di integrazione.

Hai correttamente sottolineato che ciò non dovrebbe escludere la verifica. Non riteniamo che il nostro test sia un test del requisito non funzionale. Invece, lo consideriamo un semplice indicatore di potenziale problema.

Non sono sicuro del tuo prodotto, ma nel nostro caso, se le prestazioni sono insufficienti, significa che è necessario molto lavoro per "risolverlo". Quindi il tempo di inversione, quando lasciamo tutto questo al QA, è orribile. Inoltre, le correzioni delle prestazioni avranno gravi ripercussioni su gran parte della base di codice, il che rende nulle le precedenti attività di controllo qualità. Tutto sommato, un flusso di lavoro molto inefficiente e insoddisfacente.

Detto questo, ecco alcuni punti ai tuoi rispettivi problemi:

  • concettualmente: è vero che non si tratta di unit test. Ma fintanto che tutti sono consapevoli del fatto che il test non dovrebbe verificare nulla che il QA dovrebbe fare, va bene.

  • Visual Studio: non posso dire nulla al riguardo, poiché non utilizziamo il framework di unit test di VS.

  • Macchina: dipende dal prodotto. Se il tuo prodotto è qualcosa di sviluppato per gli utenti finali con macchine desktop individuali personalizzate, è in effetti più realistico eseguire i test su macchine di sviluppatori diversi. Nel nostro caso, forniamo il prodotto per una macchina con una determinata specifica ed eseguiamo questi test delle prestazioni solo su tale macchina. In effetti, non ha molto senso misurare le prestazioni sulla tua macchina sviluppatore dual-core, quando alla fine il client eseguirà 16 core o più.

  • TDD: Sebbene l'errore iniziale sia tipico, non è un must. In effetti, scrivere questi test in anticipo lo rende più un test di regressione piuttosto che un test unitario tradizionale. Che il test abbia successo presto non è un problema. Ma ottieni il vantaggio che ogni volta che uno sviluppatore aggiunge funzionalità che rallentano le cose, perché non era a conoscenza del requisito di prestazioni non funzionali, questo test TDD lo individuerà. Succede molto ed è un feedback fantastico. Immagina che nel tuo lavoro quotidiano: scrivi codice, lo commetti, vai a pranzo e quando torni, il sistema di generazione ti dice che questo codice quando eseguito in un ambiente di carico pesante è troppo lento. È abbastanza carino per me accettare che il test TDD non sia inizialmente fallito.

  • Run-time: come detto, non eseguiamo questi test su macchine sviluppatore, ma piuttosto come parte del sistema di compilazione in una sorta di test di integrazione.


3

Sono per lo più in linea con il tuo pensiero. Sto solo mettendo il mio ragionamento con flusso indipendente.

1. Fallo funzionare prima di renderlo migliore / più veloce
Prima che il codice fornisca qualsiasi misura delle prestazioni (per non parlare della garanzia) dovrebbe essere prima corretto, cioè farlo funzionare funzionalmente. L'ottimizzazione del codice che è funzionalmente sbagliato non è solo una perdita di tempo, ma ostacola lo sviluppo.

2. Le prestazioni di un sistema hanno senso solo su un sistema completo
In genere, qualsiasi prestazione significativa dipende sempre da una determinata infrastruttura e dovrebbe essere vista solo in un sistema completo. Ad esempio, durante il finto test se il modulo riceve risposte da un file di testo locale ma in ambiente di produzione recupera dal database, il tuo precedente

3. Il ridimensionamento delle prestazioni dovrebbe essere eseguito in base all'obiettivo
Una volta che si dispone del sistema funzionale, è necessario analizzare le prestazioni del sistema e trovare i colli di bottiglia per capire dove è necessario aumentare le prestazioni. Cercare ciecamente di ottimizzare ogni metodo anche prima di conoscere le prestazioni di un sistema completo può comportare una quantità inutile di lavoro (ottimizzazione dei metodi che non contano) e può creare il codice inutilmente gonfio.

Non sono a conoscenza della funzionalità di Visual Studio, ma in genere è necessario uno strumento di profilazione più ampio.


2

Ho avuto un compito simile qualche tempo fa e la soluzione finale era da qualche parte nel mezzo tra test unitari e test delle prestazioni automatizzati in piena regola.

Alcune considerazioni in nessun ordine particolare, che possono essere utili:

  • I test delle prestazioni del QA erano ad alta intensità di lavoro e avevano un programma proprio (diciamo, una volta nell'iterazione), quindi colpire il controllo del codice sorgente non era un problema.
  • Il nostro sistema era ampio e modulare, i test unitari erano troppo granulari per le nostre esigenze e abbiamo creato speciali test unitari "grassi", realizzati con cura per innescare problemi di prestazioni nelle specifiche aree di interesse (erano anche classificati, ma questo è un dettaglio di attuazione).
  • Si applicano ancora i vincoli usuali per i test unitari: dovrebbero essere piccoli, veloci e mirati.
  • Per escludere l'influenza del framework di test, erano eseguiti da un wrapper speciale, quindi sapevamo esattamente quanto tempo impiegava l'operazione.
  • È possibile scriverli prima che l'implementazione effettiva sia completa (i risultati possono essere irrilevanti o utili, a seconda del processo, forse gli sviluppatori stanno ancora sperimentando l'implementazione e vorrebbero vedere come sta andando nel complesso).
  • Stavano funzionando dal server CI dopo ogni build, quindi il tempo di esecuzione totale dovrebbe essere relativamente breve (se non è così, diventa notevolmente più difficile individuare l'esatta modifica che ha innescato il problema).
  • Il server CI era potente e aveva il suo hardware riparato, quindi abbiamo contato questo come macchina dedicata (è possibile usare un server davvero dedicato usando un agente di build remoto).
  • Il wrapper di test ha raccolto tutte le informazioni pertinenti (specifiche hardware, nomi / categorie di test, carico del sistema, tempo trascorso, ecc.) E le ha esportate come report o nel database.
  • Abbiamo avuto un gadget per JIRA che estraeva quei report e tracciava grafici carini per nome / categoria / numero di build con alcuni controlli (sovrapponi la versione precedente all'attuale, ecc.), In modo che gli sviluppatori possano vedere rapidamente il loro impatto e i manager possano avere una panoramica (un po 'di rosso, tutto verde, sai, è importante per loro).
  • È stato possibile analizzare come procede il progetto nel tempo utilizzando le statistiche raccolte.

Quindi, alla fine, avevamo un sistema scalabile, flessibile e prevedibile che possiamo sintonizzare rapidamente per i nostri requisiti speciali. Ma ha richiesto un certo sforzo per implementare.

Tornando alle domande. Concettualmente i test unitari non sono per quello, ma puoi sfruttare le funzionalità del tuo framework di test. Non ho mai considerato i timeout dei test come un mezzo per misurare, è solo una rete di sicurezza per blocchi e cose del genere. Ma se il tuo approccio attuale funziona per te, allora continua ad usarlo, sii pratico. Puoi sempre andare in giro più tardi in caso di necessità.


0

Penso che tu stia bene. Questo è esattamente il punto di avere i timeout del test unitario: per verificare se qualcosa sta procedendo , molto più a lungo di quanto dovrebbe. Esistono limiti a questo approccio, ma sembri esserne già a conoscenza, quindi fintanto che tieni a mente tali limiti, non vedo alcun problema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.