Unità testando l'astrazione del filo

5

Essendo un codice C ++ portatile, il nostro progetto ha uno strato di astrazione sui thread. Questo strato ha dei test unitari. Questo include test come Thread::sleep(100) che si attiva in 130 ms, che CondVar::notify() riattiva l'altro thread in 50 ms e simili. Ovviamente questo a volte fallisce, specialmente quando il computer sta anche facendo qualcos'altro.

È normale testare queste cose? C'è un modo per testarli e far passare i test in modo affidabile per un'integrazione continua, in cui il server potrebbe eseguire una seconda build in parallelo, quindi è fare anche qualcos'altro.

    
posta Jan Hudec 18.05.2017 - 13:09
fonte

3 risposte

5

I test dovrebbero essere deterministici e definitivi. Supponendo che siano conformi alle specifiche, un test fallito dovrebbe indicare un bug. In caso contrario - se il test può fallire in un caso che non è un errore e non è un errore del codice - allora il test non ti dice nulla di utile se non quello che potresti assumere troppo sul tuo ambiente.

Il grosso problema qui è che al di fuori di un sistema in tempo reale, non si può garantire nulla su quanto velocemente un thread si sveglierà una volta che il pisolino è finito. Un thread a cui è stato detto di dormire per 100 ms dormirà per almeno 100 ms. Ma potrebbe ben dormire per dieci secondi se il sistema è veramente occupato. Nella maggior parte degli ambienti, non è un errore e non dovrebbe essere trattato come uno.

    
risposta data 18.05.2017 - 16:04
fonte
2

Obviously this occasionally fails.

Se è accettabile che un test fallisca occasionalmente, ciò che stai testando non è un requisito reale.

Is it normal to test these things?

Sembra che tu abbia creato un adattatore e i test che sei descrivendo non sembrano test di unità, sembrano test di integrazione.

Un motivo per la creazione di un adattatore è di abilitare il test dell'unità di qualcosa che sarebbe stato difficile da testare diversamente. Ad esempio, si desidera verificare che alcune funzioni di livello superiore chiamino sleep() quando si suppone che facciano, ma non c'è modo di iniettare un doppio di test per la funzione statica sleep () del sistema operativo. Quindi, crei un adattatore --- un oggetto molto sottile con un metodo sleep(n) che chiama la funzione sleep(n) statica. In questo modo, puoi inserire un doppio per l'adattatore nel test dell'unità della funzione di livello superiore.

È difficile creare un test unitario per l'adattatore stesso, quando lo scopo è quello di creare un'interfaccia testabile per unità a qualcosa che non fosse testabile da unità per cominciare.

Solitamente, si rende l'adattatore così insignificante, che tutti possono essere d'accordo sul fatto che l'adattatore stesso non ha bisogno di essere testato.

    
risposta data 18.05.2017 - 17:53
fonte
1

Con Sleep (n), due regressioni posso immaginare che un test sia utile da catturare sono:

  1. Mancato richiamo della piattaforma sottostante in modalità sleep con conseguente n il sonno si verifica affatto
  2. Le unità di ritardo vengono tradotte in modo errato (ad esempio 100 secondi anziché 100 millisecondi).

Se il rischio che si verifichi un tale regresso vale la pena di scrivere test dipende dal giudizio.

I test come questi sono sensibili al timing e passano o falliscono a causa della velocità attuale dell'ambiente su cui sono in esecuzione. Per renderli affidabili, sei costretto ad aumentare i ritardi. Ad esempio, testare Sleep (10) per assicurarsi che non sia realmente Sleep (0) o Sleep (10000) non può essere fatto in modo affidabile testando che il ritardo sia tra 10ms e 20ms. se l'ambiente è lento, tale test è suscettibile di passare anche se il bug n. 1 esiste (falso positivo) e fallisce anche se non è presente alcun bug (falso negativo).

I test unitari dovrebbero essere affidabili e ripetibili su qualsiasi macchina, specialmente su macchine sviluppatrici.

L'unico modo per rafforzare l'affidabilità del test è quello di spingere i ritardi testati molto al di sopra della velocità tipica più bassa dell'ambiente. Ad esempio, verificare che Sleep (5000) causi un ritardo tra 5000 ms e 6000 ms. Ma ora i test sono lenti da eseguire.

I test unitari dovrebbero essere veloci, in modo che gli sviluppatori non siano scoraggiati dal farli spesso.

Potrebbe essere possibile trovare un saldo in modo che i test siano ragionevolmente veloci e affidabili, ma con scansioni casuali di antivirus, aggiornamenti, specifiche e chissà su cos'altro succederà sui computer degli sviluppatori potrebbe essere più facile non includerli come test unitari, ma fateli funzionare come una suite separata.

Questo argomento tocca anche i componenti multi-thread test. Puoi scrivere il componente in modo tale da renderlo controllabile sotto test, ma io tendo a non gradirlo perché tende a rendere il design più complesso solo per i test che violano il principio del bacio. Spesso esiste un modo più semplice per testare un componente multi-thread, ma per garantire l'affidabilità sono necessari grandi ritardi nei test. Questo metodo è spesso odiato perché tali test tendono ad essere inclusi nella suite di test unitari e improvvisamente gli sviluppatori scoprono che i test unitari ora richiedono più anni, a volte alcuni test falliscono casualmente, causando a qualcuno di "risolverli" i test ora richiedono ancora più tempo.

Ne parlo perché questi test potrebbero andare nella stessa suite a esecuzione lenta in esecuzione sull'ambiente controllato. Ed essere fatti su misura per girare in modo affidabile su una macchina di prova a loro dedicata che non aveva nient'altro da fare, forse lanciata da CI. Questa suite di test potrebbe anche includere altri tipi di test a bassa velocità che non sono necessariamente affidabili, come i test di randomizzazione che sono sottovalutati. Il tipo di test utili che vedo mancanti perché nessuno sa dove metterli.

    
risposta data 19.05.2017 - 12:30
fonte

Leggi altre domande sui tag