martedì 6 marzo 2012

Test asincrono e negativi

La fotografia che descrive questo articolo è una sonda di temperatura per il controllo, naturalmente, differenziali di temperatura.
Test-Driven Development è una tecnica che può essere applicato a qualsiasi livello di dettaglio: non solo alle classi e ai piccoli gruppi di oggetti, ma anche a livello di sistema. In tal caso, si scrive end-to-end test che coprono l'input dell'utente e il risultato finale, la risposta ottiene o alcuni effetti collaterali.
A volte, questi test sono asincroni in quanto il sistema ha alcuni meccanismi interni che vogliamo incapsulare . Ad esempio potremmo voler verificare che un video viene aggiunto a una playlist, e le miniature vengono estratti.
Poiché questa è un'operazione CPU, è comunemente spostato in background una volta che il video è stato ricevuto. La richiesta POST viene poi rispose immediately.If vogliamo eseguire una affermazione, quindi, non possiamo controllare solo dopo essere stato risposto da parte del sistema in prova, dal momento che l'operazione non è ancora finita.
Controllando, intendo effettuare una nuova richiesta GET alla pagina playlist, o in ogni caso chiedere nuovamente il riconoscimento del suo stato. Dobbiamo aspettare un non-deterministico quantità di tempo prima di affermare qualcosa.
Quindi, quanto dovremmo aspettare?

Fowler I due metodi di prova asincrona

Un anti-pattern per risolvere questi problemi si chiama sonno nuda : si tratta di aspettare che X (milli) secondi con una sleep () chiamata prima di fare un'affermazione.
Questo approccio renderebbe il test ha esito negativo a volte, a seconda del carico del sistema o altre condizioni incontrollabili, il tempo di attesa dovrebbe anche essere sintonizzati per ogni macchina. E 'una questione di fortuna per un test come questo a passare, a meno che non abbiamo messo un tempo molto alto di attesa che rallenterà la suite di test fino a quando non vogliono ore per eseguire tutti i test insieme.
Martin Fowler descrive due modelli per l'eliminazione di questi test traballante.
Il primo è il ciclo di controllo : i controlli vengono eseguiti più volte per la presenza di una risposta, e il test fallisce solo quando il risultato non è corretto, o non arrivare in un timeout lungo:

01.int limit = 30 ;
02.int trascorso = ;
03.mentre (limite trascorso <) {
04.risultato bool = makeAssertion ();
05.se (result) {
06.rompere ;
07.}
08.trascorso + +;
. 09sleep ( );
10.}
11.se (! result) {
12.fail ( "Dopo 30 secondi il risultato atteso non è stato prodotto." );
13.}
In uno scenario verde, il test viene superato il più presto possibile, data una frequenza sufficientemente alta polling. In uno scenario rosso, fallirebbe dopo il tempo limite è raggiunto. Questo dovrebbe essere un evento raro.
Il secondo schema è il callback : il sistema fornisce un meccanismo di sincronizzazione, sia per scopi di integrazione o anche per l'utente finale, per sapere quando un'azione asincrona è terminato. Ad esempio, un registro può essere scritto o una mail possono essere inviate all'utente quando il suo video ha guadagnato miniature ed è stato finalmente aggiunto all'elenco di riproduzione scelto. Ecco un esempio in Java, supponendo che il sistema di mailing è stato deriso:

01./ / setup
02.int timeout = 30 ;
03.finale AssertionToken synchronizationObject = nuovo AssertionToken (); / / una classe vuota
04./ / dentro il finto mailer, chiamato quando l'operazione è terminata
05.pubblica vuoto send (String address, ...) {
06.sincronizzato (synchronizationObject) {
. 07synchronizationObject.notify ();
08.}
09.}
10./ / nella prova
11.sincronizzato (synchronizationObject) {
12.synchronizationObject.wait (timeout), / / anche per intercettare le eccezioni
13.}
14.assertEquals (...);

Metodo di Nat Price per i test negativi

Uno degli autori di crescente Object-Oriented Software descrive in uno dei suoi discorsi ulteriori esempi di meccanismi di sincronizzazione per fare affermazioni, che possono anche essere cross-thread (sincronizzazione sul ciclo degli eventi Swing).
Una delle tecniche spiegate comporta test negativi: come testare, in modo asincrono, che qualcosa * non * accada.
La logica di base di questa tecnica è:
  1. fare (X).
  2. non può affermare che effetto (X) non avviene in una quantità finita di tempo. Quindi fare (Y).
  3. affermare effetto (Y) è accaduto .
  4. affermare effetto (X) non è accaduto : a causa di ordini, avrebbe potuto solo effetto accaduto prima (Y). È ora possibile effettuare un'asserzione.
Per fissare le idee, vediamo un esempio di vita reale, in Erlang codice. Questo test coinvolge due nodi, che sono probabilmente diversi processi su macchine diverse, Milano e Genova. 
Il test deve verificare che, quando chiediamo un record che comincia con "a", abbiamo solo due risposte: una da Milano e uno da Genova. Genova è stata passata la richiesta: ogni nodo alluvioni agli altri conosce una richiesta di lettura, prima di tentare di rispondere.
La richiesta deve smettere dopo che ha raggiunto entrambi i nodi, e non essere inondata di nuovo a Milano, a Genova e così via all'infinito.

01.flood_without_loops (Milano) >
. 02Genoa tuplenode: init (),
03.tuplenode: ingresso (Milano, {a, }),
. 04tuplenode: ingresso (Genova, {a, }),
05.tuplenode: addFloodTarget (Milano, Genova),
. 06tuplenode: addFloodTarget (Genova, Milano),
07.tuplenode: readNonBlocking (Milano, startingWith (a), 1001 ),
08.ricevere
09.{Promise1, _} ?> assertEqual ( 1001 , Promise1)
10.fine,
11.ricevere
12.{Promise2, _} ?> assertEqual ( 1001 , Promise2)
13.fine,
14.che cosa fare adesso?
Non possiamo provare che non deterministicamente altri messaggi vengono ricevuti. Anche se aspettiamo, non saremo sicuri che i messaggi non verranno inviati: potrebbero essere in viaggio tra Genova e Milano. Dal codice di produzione (non mostrato) siamo solo sicuri che le inondazioni hanno luogo prima della trasformazione richiesta: quindi se la richiesta è stata inondata di nuovo a Milano, è stato inviato entro la fine del test a causa di ordinare, dal momento che Genova ha risposto. Ma non possiamo aspettare un messaggio a * non * arrivano.
In generale, non possiamo fare isolate, negativi, affermazioni asincroni perché l'evento si vuole evitare può avvenire subito dopo l'affermazione è stata fatta (o lontano nel futuro.)
Così il problema di fondo è: come aspettare abbastanza a lungo per essere sicuri che il messaggio errato non è stato inviato, ma quanto basta per non correre un lento, test traballante? 
Pryce presupposto è che le richieste vengono elaborate in ordine. Se si può garantire che in questo sistema, possiamo aggiungere questo codice alla prova:

1.tuplenode: readNonBlocking (Genova, startingWith (a), 1002 ),
2.ricevere
3.{Promise3, _} ?> assertEqual ( 1002 , Promise3)
4.fine,
5.ricevere
6.{Promise4, _} ?> assertEqual ( 1002 , Promise4)
7.fine.
Abbiamo emettere una nuova richiesta con id 1002 a Genova . Riceviamo tutti i messaggi che arrivano (senza filtro), ei prossimi due deve avere id 1002, corrispondente alla nuova richiesta.
Dal momento che:
  • Nodi eseguito su un ciclo singolo evento (a la Node JS);
  • i messaggi vengono inviati in ordine;
Il messaggio di lettura allagata 1002 saranno trattati dal Milan solo dopo che il precedente 1001, dato 1001 è stato invaso da Genova prima dell'invio Promise2 {,} _. Quindi, se stiamo ricevendo {Promise3, _} e {Promise4, _} uno di essi deve venire da Milano, poi, 1001 non c'è più o avrebbe ritardato le due risposte per 1002.

Nessun commento:

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.