2014-09-29 17 views
18

Vorrei interromperlo e contrassegnare come test di junit non eseguiti troppo a lungo (eseguiti con la build di Maven 3). Sono a conoscenza di tre modi di farlo:Contrassegna come non riuscito per troppo tempo JUnit test

1) con l'annotazione di prova con parametro di timeout:

@Test(timeout=100) 
public void testWithTimeout() { 
    ... 
} 

2) usando la regola della nota:

@Rule 
public Timeout globalTimeout = new Timeout(100); 

3) Configurazione di Maven-surefire- plugin usando le seguenti opzioni:

forkedProcessTimeoutInSeconds=1 
reuseForks=false 

L'indizio è 1) e 2) richiede di cambiare ogni test (fa male quando ne hai molte migliaia). 3) la soluzione non è accettabile poiché all'interno di molti moduli il primo test avvia il contesto che viene utilizzato dal test - le prestazioni dei test diminuirebbero drasticamente.

Avete altre idee su come raggiungerlo? Qualche soluzione complicata (che non implichi il patching di JUnit :))?

+0

La mia prima domanda sarebbe: quali sono i tuoi test unitari che fanno quel risultato in così tanti timeout? Sembra che tu possa effettivamente eseguire test di integrazione o di accettazione. –

+0

Esattamente, hai ragione. Si prega di notare che non ho menzionato che sto usando test unitari. JUnit è usato come base per l'integrazione e test funzionali. –

+2

No, ma Maven generalmente consiglia Failsafe per i test di integrazione/funzionali: qui è possibile trovare opzioni migliori. –

risposta

0

In realtà, la regola di timeout applica lo stesso timeout a tutti i metodi di test in una classe.

Se non si desidera aggiungerlo a ogni classe di test, è possibile definirlo una volta in una classe di base di test.

Per qualcosa che non richiede una modifica a tutte le classi, è possibile implementare un corridore suite di Junit personalizzato (si veda secondo esempio di codice)

Solo per divertimento che ne dici di questa soluzione hacky utilizzando il metodo Thread.stop deprecato ?

Prima che la funzione avvii un thread di controllo per ogni test, che dopo un timeout uccide il thread di test di Junit.

Se il test è completo, la pulizia uccide il thread di controllo.

Funziona per questa piccola dimostrazione, non so se questo funzionerebbe su scala di produzione.

import static org.junit.Assert.*; 

import org.junit.After; 
import org.junit.Before; 
import org.junit.Test; 

// You could also make this a base class for test classes 
public class TimeoutTest { 

    Thread watcherThread ; 
    Thread junitTestThread; 
    final static int testTimeout = 2000; 

    @Before 
    public void myInit() 
    { 
     junitTestThread = Thread.currentThread(); 
     watcherThread = new Thread() 
     { 
      @Override 
      public void run() 
      { 
        try { 
         Thread.sleep(testTimeout); 
         junitTestThread.stop(); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
      } 
     }; 
     watcherThread.start(); 
    } 

    @After 
    public void myCleanup() throws InterruptedException 
    { 
     watcherThread.stop(); 
    } 

    @Test 
    public void testPassingFastTest() throws InterruptedException { 
     Thread.sleep(1000); 
    } 

    @Test 
    public void testFailingSlowTest() throws InterruptedException { 
     Thread.sleep(3000); 
    } 

} 

O di fare questo per diverse classi di test utilizzando una suite:

import java.util.Arrays; 

import org.junit.runner.Description; 
import org.junit.runner.RunWith; 
import org.junit.runner.notification.RunListener; 
import org.junit.runner.notification.RunNotifier; 
import org.junit.runners.Suite; 
import org.junit.runners.Suite.SuiteClasses; 
import org.junit.runners.model.InitializationError; 
import org.junit.runners.model.RunnerBuilder; 

@RunWith(AllTests.class) 
public class AllTests extends Suite{ 

    Thread watcherThread ; 
    Thread junitTestThread; 
    final static int testTimeout = 70; 

    public AllTests(final Class<?> clazz) throws InitializationError { 
     super(clazz, findClasses()); 
     } 

    private static Class<?>[] findClasses() { 
     // You could write code here to get the list of all test classes from specific directories 
     return new Class<?>[] {TimeoutTest.class,TimeoutTest2.class}; 
     } 


    @Override 
    public void run(final RunNotifier notifier) { 


     notifier.addListener(new RunListener() { 
     @Override 
     public void testStarted(final Description description) {    
      System.out.println("Before test " + description.getDisplayName()); 
      junitTestThread = Thread.currentThread(); 
      watcherThread = new Thread() 
      { 
       @Override 
       public void run() 
       { 
         try { 
          Thread.sleep(testTimeout); 
          junitTestThread.stop(); 
         } catch (InterruptedException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
         } 
       } 
      }; 
      watcherThread.start(); 

     } 

     @Override 
     public void testFinished(final Description description) { 
      System.out.println("After test " + description.getDisplayName()); 
      watcherThread.stop(); 

     } 

     } 
    ); 

    super.run(notifier); 


    }  
} 
+0

Inoltre, se le tue classi di test hanno una classe base comune, puoi aggiungere questo codice al Prima e al Dopo di quella classe –

0

Forse non proprio la soluzione che state cercando, ma se avete i test in esecuzione in un build server continua, non ci di solito è un timeout globale per il lavoro. e.g. for Jenkins

2

Si può provare a definire il proprio runner di test scrivendo una classe che estende BlockJunit4ClassRunner e fallisce se l'esecuzione del test supera il timeout definito. Quindi annota le tue classi di test con @RunWith (CustomizedTestRunner.class) Hai ancora bisogno di modificare le classi ma il valore di timeout può essere specificato in una singola posizione.

+0

Sono d'accordo. Ho sempre creato un runner personalizzato per centralizzare la configurazione del test con buoni risultati! –