2009-08-04 20 views
38

Come utilizzare junit per eseguire un test di concorrenza?Come eseguire il test dell'unità di concorrenza?

Diciamo che ho una classe

public class MessageBoard 
{ 
    public synchronized void postMessage(String message) 
    { 
     .... 
    } 

    public void updateMessage(Long id, String message) 
    { 
     .... 
    } 
} 

I wan per testare l'accesso multiplo per questo postMessage contemporaneamente. Qualche consiglio su questo? Desidero eseguire questo tipo di test di concorrenza contro tutte le mie funzioni di setter (o qualsiasi metodo che comporti un'operazione di creazione/aggiornamento/cancellazione).

risposta

18

Purtroppo non credo che è possibile in modo definitivo dimostrare che il codice è thread-safe utilizzando test di runtime. Puoi lanciare tutti i fili che vuoi contro di esso, e può/non può passare a seconda della programmazione.

Forse dovresti esaminare alcuni strumenti di analisi statica, come PMD, che possono determinare come stai utilizzando la sincronizzazione e identificare i problemi di utilizzo.

+3

+1 Per suggerire l'uso di un analizzatore di codici statici. Una ricerca su Google rivela una serie di possibilità [analisi statica della concorrenza di java]. –

+0

http://channel9.msdn.com/shows/Going+Deep/CHESS-An-Automated-Concurrency-Testing-Tool/ – inf3rno

8

È possibile solo verificare la presenza di bug simultanei, non la loro assenza.

Tuttavia è possibile scrivere un programma di prova specializzato che genera più thread simultanei e quindi chiama i metodi annotati @Test.

+16

> (Con test) "È possibile provare solo la presenza di bug simultanei, non la loro assenza. " In senso generale, ciò vale anche per i bachi non concorrenti. – Thilo

+4

Questo è un po 'vero, ma con i test unitari tradizionali è possibile dimostrare l'assenza (attuale) di * specifici * bug. Quando si effettuano test per problemi di concorrenza, non si ottiene nemmeno tanto. –

0

TestNG ha il supporto per i test di concorrenza in Java. Questo article descrive come può essere usato e ci sono documenti sul sito testng.

Non so se si può fare lo stesso test, allo stesso tempo, però

0

L'approccio migliore è quello di utilizzare i test di sistema per saltare il vostro codice con le richieste per vedere se cade. Quindi utilizzare il test dell'unità per verificare la correttezza logica. Il modo in cui mi avvicinerei a questo è creare un proxy per la chiamata asincrona e farlo sincronizzare in fase di test.

Tuttavia, se si desidera eseguire questa operazione a un livello diverso da un ambiente di installazione completo e completo, è possibile farlo in junit creando il proprio oggetto in un thread separato, quindi creare molti thread che generano richieste al proprio oggetto e bloccare il thread principale fino al suo completamento. Questo approccio può far fallire il test a intermittenza se non lo si fa nel modo giusto.

+0

Sfortunatamente, questo approccio è molto inefficace nello scoprire bug, dal momento che eseguire ripetutamente lo stesso test di solito verifica lo stesso thread interleaving più e più volte. –

+0

In quale altro modo faresti questo? Una suite completa di test di sistema che rappresentano gli scenari dei clienti troverà gli errori che il cliente vedrebbe. –

7

In .NET esistono strumenti come TypeMock Racer o Microsoft CHESS progettati specificamente per la concorrenza di unità di test. Questi strumenti non solo trovano bug multithreading come deadlock, ma forniscono anche l'insieme di interfogli di thread che riproducono gli errori.

Immagino che ci sia qualcosa di simile per il mondo Java.

1

Nel tuo esempio il metodo postMessage() è sincronizzata, in modo da non effettivamente vedere gli eventuali effetti di concorrenza da all'interno di una singola macchina virtuale, ma si potrebbe essere in grado di valutare le prestazioni della versione sincronizzata.

Sarà necessario eseguire più copie del programma di test contemporaneamente in diverse macchine virtuali. È possibile utilizzare

Se non è possibile ottenere il framework di test per farlo, è possibile avviare alcune VM. La roba costruttore processo è un dolore con percorsi e quant'altro, ma qui è il disegno generale:

Process running[] = new Process[5]; 
for (int i = 0; i < 5; i++) { 
ProcessBuilder b = new ProcessBuilder("java -cp " + getCP() + " MyTestRunner"); 
running[i] = b.start(); 
} 

for(int i = 0; i < 5; i++) { 
running[i].waitFor(); 
} 

faccio di solito qualcosa di simile per semplici test filettate, come altri hanno scritto, il test non è una prova di correttezza , ma di solito scuote bug stupidi nella pratica. Aiuta a testare per un lungo periodo in una varietà di condizioni diverse - a volte i bug della concorrenza richiedono un po 'di tempo per manifestarsi in un test.

public void testMesageBoard() { 
final MessageBoard b = new MessageBoard(); 

int n = 5; 
Thread T[] = new Thread[n]; 
for (int i = 0; i < n; i++) { 
    T[i] = new Thread(new Runnable() { 
    public void run() { 
    for (int j = 0; j < maxIterations; j++) { 
     Thread.sleep(random.nextInt(50)); 
     b.postMessage(generateMessage(j)); 
     verifyContent(j); // put some assertions here 
    } 
    } 
    }); 

    PerfTimer.start(); 
    for (Thread t : T) { 
    t.start(); 
    } 

    for (Thread t : T) { 
    t.join(); 
    } 
    PerfTimer.stop(); 
    log("took: " + PerfTimer.elapsed()); 
} 
}**strong text** 
1

Il test dei bug di concorrenza è impossibile; non devi solo convalidare le coppie di input/output, ma devi convalidare lo stato in situazioni che potrebbero verificarsi o meno durante i test. Sfortunatamente JUnit non è attrezzato per farlo.

+1

forse non sapevano che è impossibile così lo hanno fatto http://channel9.msdn.com/shows/Going+Deep/CHESS-An-Automated-Concurrency-Testing-Tool/ – inf3rno

15

Si consiglia di utilizzare MultithreadedTC - Scritto dallo stesso concorrency Bill Pugh (e Nat Ayewah). Citazione da loro overview:

MultithreadedTC è un framework per testare applicazioni simultanee. È dispone di un metronomo che viene utilizzato per fornire un controllo preciso sulla sequenza di attività in più thread.

Questo quadro permette di testare in modo deterministico ogni interleaving thread nel test separati

1

È possibile utilizzare la libreria tempus fugit-per eseguire i metodi di prova in tempi paralleli e multiple per simulare un ambiente carico di prove di tipo. Sebbene un commento precedente evidenzi che il metodo post sia sincronizzato e protetto, possono essere coinvolti membri o metodi associati che non sono protetti, quindi è possibile che un test di tipo carico/ammollo possa essere rilevato da. Ti suggerisco di impostare un test abbastanza approssimativo con grana fine/fine-fine per darti la migliore possibilità di catturare i buchi del loop.

Vedere la sezione di integrazione JUnit di documentation.

BTW, uno sviluppatore su detto progetto :)

+0

La mia esperienza con questo tipo di strategia di test è che quando il test fallisce non ci sono abbastanza informazioni diagnostiche prodotte per rintracciare il bug, così si finisce con un test che fallisce in modo irregolare e che non si vuole investigare. @Judah Himango suggerisce librerie che possono aiutare con questo problema. – Spina

+0

corretto (e obbligato a digitare almeno 15 caratteri) – Toby

1

prova a guardare ActiveTestSuite che le navi con JUnit. Può contemporaneamente avviare più test JUnit:

public static Test suite() 
{ 
    TestSuite suite = new ActiveTestSuite(); 
    suite.addTestSuite(PostMessageTest.class); 
    suite.addTestSuite(PostMessageTest.class); 
    suite.addTestSuite(PostMessageTest.class); 
    suite.addTestSuite(PostMessageTest.class); 
    suite.addTestSuite(PostMessageTest.class); 
    return suite; 
} 

Quanto sopra verrà eseguito lo stesso JUnit classe di test 5 volte in paralell. Se volevi una variazione nei tuoi test paralell, crea semplicemente una classe diversa.

5

L'esecuzione contemporanea può determinare risultati imprevisti. Ad esempio, ho appena scoperto che mentre la mia suite di test con 200 test passava quando eseguita a uno a uno, fallisce per l'esecuzione simultanea, l'ho scavata e non era un problema di sicurezza del thread, ma un test dipendente da un altro, che è una brutta cosa e potrei risolvere il problema.

Mycila work on JUnit ConcurrentJunitRunner and ConcurrentSuite è molto interessante. L'articolo sembra un po 'obsoleto rispetto all'ultima versione di GA, nei miei esempi mostrerò l'utilizzo aggiornato.

Annotazione una classe di test come la seguente causerà per eseguire metodi di prova contemporaneamente, con un livello di concorrenza di 6:

import com.mycila.junit.concurrent.ConcurrentJunitRunner; 
import com.mycila.junit.concurrent.Concurrency; 

@RunWith(ConcurrentJunitRunner.class) 
@Concurrency(6) 
public final class ATest { 
... 

è anche possibile eseguire tutte le classi di test contemporaneamente:

import com.mycila.junit.concurrent.ConcurrentSuiteRunner; 

@RunWith(ConcurrentSuiteRunner.class) 
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class}) 
public class MySuite { 
} 

il Maven dependency è:

<dependency> 
    <groupId>com.mycila</groupId> 
    <artifactId>mycila-junit</artifactId> 
    <version>1.4.ga</version> 
</dependency> 

sono attualmente indagando su come eseguire i metodi più volte e contemporaneamente con questo pacchetto. Potrebbe essere già possibile, se qualcuno ha un esempio fammi sapere, sotto la mia soluzione homebrewed.

@Test 
public final void runConcurrentMethod() throws InterruptedException { 
    ExecutorService exec = Executors.newFixedThreadPool(16); 
    for (int i = 0; i < 10000; i++) { 
     exec.execute(new Runnable() { 
      @Override 
      public void run() { 
       concurrentMethod(); 
      } 
     }); 
    } 
    exec.shutdown(); 
    exec.awaitTermination(50, TimeUnit.SECONDS); 
} 

private void concurrentMethod() { 
    //do and assert something 
} 

Come altri hanno notato, è vero che non si può mai essere sicuri se un bug concorrenza rivelerebbe o no, ma con decine di migliaia o centinaia di migliaia di esecuzioni con la concorrenza di, diciamo 16, le statistiche è dalla tua parte.

+0

FYI, in base alla soluzione homebrewed, è possibile implementare facilmente la regola JUnit personalizzata in modo simile a [questo articolo] (http://www.codeaffine.com/2013/04/ 10/running-junit-tests-ripetutamente-senza-loop /) BTW sto provando a fare lo stesso ora :) – Yura

0

Puoi anche provare HavaRunner. Esegue test in parallelo per impostazione predefinita.

0

È possibile controllare IMUnit. È compatibile con JUnit.

Problemi correlati