Il presente articolo è stato tradotto automaticamente.

Programmazione asincrona

Unit test codice asincrono: Tre soluzioni per test migliore

Sven Grand

Scaricare il codice di esempio

Programmazione asincrona è diventata sempre più importante durante l'ultimo decennio. Se per parallelismo basato su CPU o concorrenza basata su IO, gli sviluppatori stanno impiegando asincronia per aiutare a sfruttare al meglio le risorse disponibili e, in definitiva, fare di più con meno.  Le applicazioni client più reattive e più scalabile applicazioni server sono tutti facilmente raggiungibili.

Gli sviluppatori di software hanno imparato un sacco di modelli di progettazione per la costruzione efficace funzionalità sincroni, ma consigliate per la progettazione del software asincroni sono relativamente nuovi, anche se il supporto è fornito dai linguaggi di programmazione e librerie per la programmazione parallela e simultanea ha migliorato drammaticamente con il rilascio di Microsoft .NET Framework 4 e 4,5. Mentre c'è già un sacco di buoni consigli per l'utilizzo di nuove tecniche (vedere "Best Practices in programmazione"asincrona presso bit.ly/1ulDCiI e "parlare: Migliori pratiche Async"a bit.ly/1DsFuMi), consigliate per progettare le API interne ed esterne per applicazioni e librerie con caratteristiche di linguaggio come async e attendono e la Task Parallel Library (TPL) sono ancora sconosciuti a molti sviluppatori.

Questa lacuna colpisce non solo le prestazioni e l'affidabilità delle applicazioni e librerie che tali sviluppatori stanno costruendo, ma anche la testabilità delle loro soluzioni, come molte delle migliori pratiche che consentono la creazione di disegni asincroni robusti abilitare più facile unit test, pure.

Con tali pratiche migliori nella mente, questo articolo sarà presentare modi per design e refactoring di codice per meglio testabilità e dimostrare come questo influenzerà le prove. Le soluzioni sono applicabili al codice che si avvale di async e attendono, così come codice basato su meccanismi multithreading di basso livello da precedenti quadri e librerie. E, nel processo, le soluzioni saranno non solo essere meglio fattorizzate per test, saranno più facilmente e in modo più efficiente consumo di utenti del codice sviluppato.

Il team che con cui lavoro è lo sviluppo di software per dispositivi medici a raggi x. In questo dominio è fondamentale che la nostra copertura di unit test è sempre ad alto livello. Recentemente, uno sviluppatore mi ha chiesto: "tu stai sempre ci spinge a scrivere unit test per tutto il nostro codice. Ma come posso scrivere ragionevole unit test, quando il mio codice inizia un altro thread o utilizza un timer che più tardi avvia un thread e lo esegue più volte?"

Questa è una domanda significativa. Supponiamo di che avere questo codice da testare:

public void StartAsynchronousOperation()
{
  Message = "Init";
  Task.Run(() =>
    {
      Thread.Sleep(1900);
      Message += " Work";
    });
}
public string Message { get; private set; }

Il mio primo tentativo di scrivere una prova per tale codice non è stato molto promettente:

[Test]
public void FragileAndSlowTest()
{
  var sut = new SystemUnderTest();
  // Operation to be tested will run in another thread.
  sut. StartAsynchronousOperation();
  // Bad idea: Hoping that the other thread finishes execution after 2 seconds.
  Thread.Sleep(2000);
  // Assert outcome of the thread.
  Assert.AreEqual("Init Work", sut.Message);
}

Mi aspetto di unit test per correre veloce e fornire risultati prevedibili, ma il test che ho scritto è stato lento e fragile. Il metodo di StartAsynchronousOperation prende il via l'operazione per essere testato in un altro thread, e la prova deve verificare l'esito dell'operazione. Il tempo che necessario per iniziare un nuovo thread o inizializzare un thread esistente, seduto nel pool di thread e di eseguire l'operazione non è prevedibile, perché dipende da altri processi in esecuzione sul computer del test. Il test può non riuscire di volta in volta quando il sonno è troppo breve e l'operazione asincrona non è ancora finito. Io sono tra il diavolo e il profondo blu del mare: Cerco di mantenere il tempo di attesa più breve possibile, con il rischio di un test di fragile o aumentare il tempo di sonno per rendere il test più affidabile, ma rallentare la prova anche di più.

Il problema è simile a quando si desidera testare il codice che utilizza un timer:

private System.Threading.Timer timer;
private readonly Object lockObject = new Object();
public void StartRecurring()
{
  Message = "Init";
  // Set up timer with 1 sec delay and 1 sec interval.
  timer = new Timer(o => { lock(lockObject){ Message += " Poll";} }, null,
    new TimeSpan(0, 0, 0, 1), new TimeSpan(0, 0, 0, 1));
}
public string Message { get; private set; }

E questa prova rischia di avere gli stessi problemi:

[Test]
public void FragileAndSlowTestWithTimer()
{
  var sut = new SystemUnderTest();
  // Execute code to set up timer with 1 sec delay and interval.
  sut.StartRecurring();
  // Bad idea: Wait for timer to trigger three times.
  Thread.Sleep(3100);
  // Assert outcome.
  Assert.AreEqual("Init Poll Poll Poll", sut.Message);
}

Quando provo una ragionevolmente complessa operazione con parecchi rami di codice diverse, finisco con un enorme numero di test separati. Il mio test suite get più lento e più lento con ogni nuovo test. I costi di manutenzione per questi test suites aumento, perché devo spendere tempo indagando i guasti sporadici. Inoltre, gruppi di test lento tendono a essere eseguito solo di rado e quindi hanno un minor numero di benefici. Ad un certo punto, sarebbe probabilmente interrompere l'esecuzione di queste lenti e non riuscendo ad intermittenza test complessivamente.

I due tipi di test illustrato in precedenza sono anche in grado di rilevare quando l'operazione genera un'eccezione. Perché l'operazione viene eseguita in un thread diverso, le eccezioni non vengono propagate al thread test runner. Questo limita la capacità del test di verifica il comportamento corretto errore del codice sotto test.

Ho intenzione di presentare tre soluzioni generali per evitare la lenta, fragile unit test migliorando il design del codice testato, e mostrerò come questo permette di unit test verificare le eccezioni. Ogni soluzione ha vantaggi, così come gli svantaggi o limitazioni. Alla fine, darò alcune raccomandazioni per quanto riguarda quale soluzione scegliere per diverse situazioni.

Questo articolo è circa gli unit test. In genere, unit test sono test di codice nell'isolamento da altre parti del sistema. Una di quelle altre parti del sistema è la capacità multithreading OS. Metodi e classi della libreria standard sono utilizzati per pianificare il lavoro asincrono, ma l'aspetto multithreading dovrebbe escludersi in unit test, che dovrebbe concentrarsi sulla funzionalità che viene eseguito in modo asincrono.

Unit test per codice asincrono ha senso quando il codice contiene blocchi di funzionalità in esecuzione in un thread e gli unit test devono verificare che i blocchi funzionino come previsto. Quando gli unit test hanno dimostrato che la funzionalità è corretta, ha senso utilizzare strategie di test aggiuntivi per scoprire problemi di concorrenza. Ci sono diversi approcci per test e analisi del codice multithreading per trovare questi tipi di problemi (vedi, ad esempio, "Strumenti e tecniche per identificare problemi di concorrenza," a bit.ly/1tVjpll). Prove di stress, per esempio, può mettere un intero sistema o gran parte del sistema sotto carico. Queste strategie sono sensibili a complemento di unit test, ma sono fuori portata per questo articolo. Le soluzioni in questo articolo verranno mostrerà come escludere le parti multithreading durante il test di funzionalità in isolamento con unit test.

Soluzione 1: Separare le funzionalità di Multithreading

La soluzione più semplice per gli unit test la funzionalità coinvolti in operazioni asincrone è di separare tale funzionalità di multithreading. Gerard Mezaros ha descritto questo approccio nel modello di oggetti umili nel suo libro, "xUnit Test pattern" (Addison-Wesley, 2007). La funzionalità di essere testato è estratta in una classe separata di nuova e il multithreading parte rimane all'interno dell'oggetto umile, che chiama la nuova classe (vedere Figura 1).

il modello di oggetto umile
Figura 1 il modello di oggetto umile

Il codice riportato di seguito viene illustrata la funzionalità estratta dopo di refactoring, ovvero codice puramente sincrono:

public class Functionality
{
  public void Init()
  {
    Message = "Init";
  }
  public void Do()
  {
    Message += " Work";
  }
  public string Message { get; private set; }
}

Prima di refactoring, la funzionalità è stata mescolata con codice asincrono, ma ho spostato alla classe di funzionalità. Questa classe può essere provata tramite semplice unit test ora perché non contiene alcuna multithreading piu '. Si noti che tale refactoring è importante in generale, non solo per gli unit test: componenti non dovrebbero esporre asincrona wrapper per operazioni intrinsecamente sincrona e dovrebbero invece lascia fino al chiamante per determinare se la chiamata di quell'operazione di offload. Nel caso di uno unit test, scelgo di non, ma l'applicazione che richiede potrebbe decidere di farlo, per motivi di tempi di risposta o di esecuzione parallela. Per ulteriori informazioni, vedere blog di Stephen Toub post, "io dovrei esporre asincrona wrapper per i metodi sincroni?" (bit.ly/1shQPfn).

In una leggera variazione di questo modello, certi metodi privati della classe SystemUnderTest possono essere resa pubbliche per consentire prove di chiamare questi metodi direttamente. In questo caso, non deve essere creato per testare la funzionalità senza alcuna ulteriore classe multithreading.

Che separa le funzionalità tramite il modello di oggetto umile è semplice e può essere fatto non solo per il codice che pianifica immediatamente lavoro asincrono una volta, ma anche per il codice che utilizza il timer. In tal caso il timer movimentazione è mantenuto nell'oggetto umile e l'operazione ricorrente viene spostato alla classe funzionalità o un metodo pubblico. Un vantaggio di questa soluzione è che prove possono verificare direttamente le eccezioni generate dal codice sotto test. Il modello di oggetto umile può essere applicato indipendentemente dalle tecniche utilizzate per pianificare il lavoro asincrono. Gli svantaggi di questa soluzione sono che non è testato il codice in umile oggetto stesso e che deve essere modificato il codice sotto test.

Soluzione 2: Sincronizzare le prove

Se il test è in grado di rilevare il completamento dell'operazione in esame, che viene eseguito in modo asincrono, che può evitare i due svantaggi, la fragilità e la lentezza. Se il test viene eseguito il codice multithreading, può essere affidabile e veloce quando il test viene sincronizzato con l'operazione prevista dal codice sotto test. Il test può concentrarsi sulla funzionalità, mentre sono ridotti al minimo gli effetti negativi dell'esecuzione asincrona.

Nel migliore dei casi, il metodo di prova restituisce un'istanza di un tipo che sarà segnalato quando l'operazione è stata completata. Il tipo di attività, che è stato disponibile in .NET Framework versione 4, risponde bene a questa esigenza, e la caratteristica async/attendono disponibile da .NET Framework 4.5 lo rende facile da comporre i compiti:

public async Task DoWorkAsync()
{
  Message = "Init";
  await Task.Run( async() =>
  {
    await Task.Delay(1900);
    Message += " Work";
  });
}
public string Message { get; private set; }

Refactoring di questo rappresenta la migliore prassi generale che aiuta in entrambe le unit test caso e in generale consumo della funzionalità asincrone esposta. Restituendo un Task che rappresenta l'operazione asincrona, un consumatore del codice è in grado di determinare facilmente quando l'operazione asincrona è completata, se non è riuscito con un'eccezione, e se ha restituito un risultato.

Questo rende gli unit test un metodo asincrono semplice come unit test un metodo sincrono. Ora è facile per il test di sincronizzare con il codice sotto test semplicemente richiamando il metodo di destinazione e in attesa per il completamento dell'attività restituita. Questa attesa può essere fatto in modo sincrono (bloccare il thread chiamante) attraverso il Task aspettare metodi, o può essere fatto in modo asincrono (utilizzando le continuazioni per evitare di bloccare il thread chiamante) con la parola chiave await, prima di controllare il risultato dell'operazione asincrona (Vedi Figura 2).

sincronizzazione tramite Async e attendono
Figura 2 sincronizzazione tramite Async e attendono

Per poter utilizzare attendono in un metodo di unit test, la prova stessa deve essere dichiarato con async nella sua firma. Dichiarazione di sonno non è più necessario:

[Test]
public async Task SynchronizeTestWithCodeViaAwait()
{
  var sut = new SystemUnderTest();
  // Schedule operation to run asynchronously and wait until it is finished.
  await sut.StartAsync();
  // Assert outcome of the operation.
  Assert.AreEqual("Init Work", sut.Message);
}

Per fortuna, le ultime versioni dell'unità principale prova quadri — MSTest, xUnit.net e NUnit — supporto async e attendono prove (vedere Stephen Clearydi blog a bit.ly/1x18mta). Loro prova corridori possono affrontare prove di attività asincrone e attendono il completamento del thread prima di iniziare a valutare le istruzioni assert. Se il corridore di prova del framework unit test non può far fronte con async compito verificare firme di metodo, il test può chiamare almeno l'attesa metodo sul compito restituito dal sistema in prova.

Inoltre, la funzionalità basata su timer può essere migliorata con l'aiuto della classe TaskCompletionSource (vedi dettagli nel download del codice). Il test può quindi attendono il completamento delle operazioni ricorrenti specifici:

[Test]
public async Task SynchronizeTestWithRecurringOperationViaAwait()
{
  var sut = new SystemUnderTest();
  // Execute code to set up timer with 1 sec delay and interval.
  var firstNotification = sut.StartRecurring();
  // Wait that operation has finished two times.
  var secondNotification = await firstNotification.GetNext();
  await secondNotification.GetNext();
  // Assert outcome.
  Assert.AreEqual("Init Poll Poll", sut.Message);
}

Purtroppo, a volte il codice sotto test non può utilizzare async e attendono, come quando sei prove codice che ha già spedito e per motivi di rottura-cambia la firma del metodo in fase di test non può essere modificata. In situazioni come questa, la sincronizzazione deve essere implementata con altre tecniche. Sincronizzazione può essere realizzato se la classe sotto test richiama un evento o chiama un oggetto dipendente quando l'operazione è stata completata. Nell'esempio viene illustrato come implementare il test quando viene chiamato un oggetto dipendente:

private readonly ISomeInterface dependent;
public void StartAsynchronousOperation()
{
  Task.Run(()=>
  {
    Message += " Work";
    // The asynchronous operation is finished.
    dependent.DoMore()
  });
}

Un altro esempio per sincronizzazione basata su evento è nel download del codice.

Il test ora può essere sincronizzato con l'operazione asincrona quando l'oggetto dipendente è sostituito da uno stub durante il test (vedere Figura 3).

sincronizzazione tramite un oggetto dipendente Stub
Figura 3 sincronizzazione tramite un oggetto dipendente Stub

Il test è di dotare lo stub di un meccanismo di notifica di thread-safe, poiché il codice stub viene eseguito in un altro thread. Nell'esempio di codice seguente prova, viene utilizzato un ManualResetEventSlim e lo stub viene generato con il RhinoMocks beffardo quadro:

// Setup
var finishedEvent = new ManualResetEventSlim();
var dependentStub = MockRepository.GenerateStub<ISomeInterface>();
dependentStub.Stub(x => x.DoMore()).
  WhenCalled(x => finishedEvent.Set());
var sut = new SystemUnderTest(dependentStub);

Il test può ora eseguire l'operazione asincrona e può attendere la notifica:

// Schedule operation to run asynchronously.
sut.StartAsynchronousOperation();
// Wait for operation to be finished.
finishedEvent.Wait();
// Assert outcome of operation.
Assert.AreEqual("Init Work", sut.Message);

Questa soluzione per sincronizzare il test con i fili testati può essere applicata al codice con determinate caratteristiche: Il codice sotto test ha un meccanismo di notifica come async e attendono o un evento normale, o il codice chiama un oggetto dipendente.

Un grande vantaggio di async e attendere la sincronizzazione è che è in grado di propagare qualsiasi tipo di risultato al client chiamante. Un particolare tipo di risultato è un'eccezione. Così il test può gestire le eccezioni in modo esplicito. Altri meccanismi di sincronizzazione possono riconoscere solo fallimenti indirettamente tramite il risultato difettoso.

Codice con funzionalità basata su timer può utilizzare async/attendono, eventi o chiamate a oggetti dipendenti per consentire prove di sincronizzarsi con le operazioni di timer. Ogni volta che termina l'operazione ricorrente, la prova viene informata e può controllare l'esito (vedere esempi nel download del codice).

Purtroppo, temporizzatori fare unit test lento anche quando si utilizza una notifica. L'operazione ricorrente che si desidera testare generalmente inizia solo dopo un certo ritardo. Il test sarà rallentata e prendere almeno il tempo di ritardo. Questo è un ulteriore svantaggio in cima il prerequisito di notifica.

Ora potrai guardare una soluzione che evita alcuni dei limiti dei due precedenti.

Soluzione 3: Prova in un Thread

Per questa soluzione, il codice sotto test deve essere preparata in modo che la prova più tardi direttamente può innescare l'esecuzione delle operazioni sullo stesso thread come prova stessa. Questa è una traduzione dell'approccio della squadra jMock per Java (vedi "Test codice multithreading" a jmock.org/threads.html).

Il sistema da testare nell'esempio seguente utilizza un oggetto di pianificazione attività iniettata per pianificare il lavoro asincrono. Al demone­strategie le funzionalità della terza soluzione, aggiunto una seconda operazione che verrà avviata una volta ultimata la prima operazione:

private readonly TaskScheduler taskScheduler;
public void StartAsynchronousOperation()
{
  Message = "Init";
  Task task1 = Task.Factory.StartNew(()=>{Message += " Work1";},
                                     CancellationToken.None,
                                     TaskCreationOptions.None,
                                     taskScheduler);
  task1.ContinueWith(((t)=>{Message += " Work2";}, taskScheduler);
}

Il sistema da testare è modificato per utilizzare un TaskScheduler separato. Durante la prova, il TaskScheduler "normale" è sostituito da un DeterministicTaskScheduler, che permette di iniziare le operazioni asincrone in modo sincrono (vedere Figura 4).

utilizzando un TaskScheduler separato in SystemUnderTest
Figura 4 utilizzando un TaskScheduler separato in SystemUnderTest

I seguenti test possono eseguire le operazioni pianificate nello stesso thread come la prova stessa. Il test inietta il deterministiche­TaskScheduler nel codice sotto test. Il DeterministicTaskScheduler non riproducono immediatamente un nuovo thread, ma sola code operazioni pianificate. Nell'istruzione successiva la RunTasksUntil­metodo Idle in modo sincrono esegue due operazioni:

[Test]
public void TestCodeSynchronously()
{
  var dts = new DeterministicTaskScheduler();
  var sut = new SystemUnderTest(dts);
  // Execute code to schedule first operation and return immediately.
  sut.StartAsynchronousOperation();
  // Execute all operations on the current thread.
  dts.RunTasksUntilIdle();
  // Assert outcome of the two operations.
  Assert.AreEqual("Init Work1 Work2", sut.Message);
}

Il DeterministicTaskScheduler esegue l'override di metodi TaskScheduler per fornire la funzionalità di pianificazione e aggiunge, tra gli altri, il metodo di RunTasksUntilIdle specificamente per il test (vedere il codice di download per i dettagli di implementazione DeterministicTaskScheduler). Come nel test di unità sincrona, stub possono essere utilizzati per concentrarsi sul solo una singola unità funzionale alla volta.

Il codice che utilizza il timer è problematico non solo perché i test sono fragili e lento. Unit test ottiene più complicato quando il codice utilizza un timer che non è in esecuzione su un thread di lavoro. Nella libreria di classi del framework .NET, ci sono timer progettato specificamente per essere utilizzato in applicazioni di interfaccia utente, come System per Windows Form o System.Windows.Threading.DispatcherTimer per applicazioni Windows Presentation Foundation (WPF) (vedere "Confrontando il Timer classi in the .NET Framework Class Library" a bit.ly/1r0SVic). Questi usare la coda di messaggi dell'interfaccia utente, che non è direttamente disponibile durante gli unit test. Il test indicato all'inizio di questo articolo non funziona per questi timer. Il test ha far girare la pompa di messaggio, ad esempio utilizzando WPF DispatcherFrame (vedere l'esempio nel download del codice). Per mantenere l'unità test semplice e chiaro quando si sta distribuendo i timer basati su interfaccia utente, devi sostituire questi timer durante la prova. Io presento un'interfaccia per timer attivare sostituendo i timer "reali" con un'implementazione in modo specifico per il test. Faccio questo anche per "thread"-basato su timer come System.Timers.Timer o System.Threading.Timer, perché poi posso migliorare gli unit test in tutti i casi. Il sistema da testare deve essere modificato per utilizzare questa interfaccia ITimer:

private readonly ITimer timer;
private readonly Object lockObject = new Object();
public void StartRecurring()
{
  Message = "Init";
  // Set up timer with 1 sec delay and 1 sec interval.
  timer.StartTimer(() => { lock(lockObject){Message += 
    " Poll";} }, new TimeSpan(0,0,0,1));
}

Introducendo l'interfaccia ITimer, posso sostituire il comportamento del timer durante la prova, come mostrato Figura 5.

uso ITimer in SystemUnderTest
Figura 5 uso ITimer in SystemUnderTest

Lo sforzo supplementare di definire l'interfaccia ITimer ripaga perché uno unit test controllando l'esito l'inizializzazione e l'operazione ricorrente può ora eseguire molto velocemente e in modo affidabile entro i millisecondi:

[Test]
public void VeryFastAndReliableTestWithTimer()
{
  var dt = new DeterministicTimer();
  var sut = new SystemUnderTest(dt);
  // Execute code that sets up a timer 1 sec delay and 1 sec interval.
  sut.StartRecurring();
  // Tell timer that some time has elapsed.
  dt.ElapseSeconds(3);
  // Assert that outcome of three executions of the recurring operation is OK.
  Assert.AreEqual("Init Poll Poll Poll", sut.Message);
}

Il DeterministicTimer è scritto specificamente per scopi di test. Esso permette il test controllare il punto nel tempo, quando viene eseguita l'azione del timer, senza attesa. L'azione viene eseguito nello stesso thread come la prova stessa (vedere il codice di download per i dettagli di implementazione DeterministicTimer). Per l'esecuzione del codice testato in un contesto di "non-test", devo implementare un adattatore ITimer per un timer esistente. Download del codice contiene esempi di adattatori per diversi di timer quadro classe biblioteca. L'interfaccia ITimer può essere adattato alle esigenze della situazione concreta e può contenere solo un sottoinsieme di tutte le funzionalità di timer specifico.

Test codice asincrono con un DeterministicTaskScheduler o un DeterministicTimer consente di spegnere facilmente multithreading durante i test. La funzionalità viene eseguita nello stesso thread come la prova stessa. L'interazione di codice di inizializzazione e codice asincrono è mantenuta e può essere testato. Un test di questo tipo, ad esempio, può controllare i valori di tempo corretto utilizzati per inizializzare un timer. Le eccezioni vengono inoltrate al test, così essi possono controllare direttamente il comportamento di errore del codice.

Conclusioni

Efficace unit testing del codice asincrono ha tre vantaggi principali: Sono ridotti costi di manutenzione per le prove; test eseguiti più velocemente; ed è ridotto al minimo il rischio di non eseguire il test piu '. Le soluzioni presentate in questo articolo possono aiutarvi a raggiungere questo obiettivo.

La prima soluzione, che separa la funzionalità dagli aspetti asincroni di un programma tramite un oggetto umile è il più generico. È applicabile per tutte le situazioni, indipendentemente da come vengono avviate discussioni. Mi consiglia di utilizzare questa soluzione per scenari asincroni molto complessi, funzionalità complessa o una combinazione di entrambi. È un buon esempio della separazione delle preoccupazioni principio di progettazione (vedere bit.ly/1lB8iHD).

La seconda soluzione, che sincronizza il test con il completamento del thread testati, possono essere applicati quando il codice sotto test fornisce un meccanismo di sincronizzazione come async e attendono. Questa soluzione ha senso quando i prerequisiti del meccanismo di notifica sono soddisfatte comunque. Se possibile, utilizzare async elegante e attendono la sincronizzazione quando non-timer di thread sono iniziati, perché le eccezioni vengono propagate alla prova. Prove con timer possono utilizzare attendono, eventi o chiamate al oggetti dipendenti. Questi test possono essere lenti quando temporizzatori hanno lunghi ritardi o intervalli.

La terza soluzione utilizza la DeterministicTaskScheduler e la DeterministicTimer, evitando così la maggior parte delle limitazioni e svantaggi delle altre soluzioni. Richiede qualche sforzo per preparare il codice sotto test, ma può essere raggiunta unità alta prova di code coverage. Il test per codice con timer può essere eseguito molto velocemente senza l'attesa per i tempi di ritardo e intervallo. Inoltre, le eccezioni vengono propagate alle prove. Questa soluzione comporta così unità robusta, veloce ed elegante suite di test combinati con alte code coverage.

Queste tre soluzioni possono aiutare gli sviluppatori di software di evitare le insidie di unit test codice asincrono. Essi può essere utilizzati per creare gruppi di test di unità veloce e robusto e coprono una vasta gamma di tecniche di programmazione parallela.


Sven Grand è un software architect ingegneria di qualità per l'unità di business radiografia diagnostica di Philips Healthcare. Ha ottenuto "prova infetti" anni fa, quando ha sentito per la prima volta parlare di sviluppo basato su test a una conferenza sul software Microsoft nel 2001. Contattarlo al sven.grand@philips.com.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Stephen Cleary, James McCaffrey, Henning Pohl e Stephen Toub