2015-08-28 20 views
10

Ho la seguente costruzione di codice:copertura del codice finally

try { 
    //some code 
} 
catch(CustomException custExc) { 
    //log 
} 
catch(CustomException2 custExc2) { 
    //log 
} 
catch(Exception exc) { 
    //log 
} 
finally { 
    //some code 
} 

ho scritto unit test: il primo riguardava la situazione quando un'eccezione non viene lanciata (eseguendo solo cercare il codice di blocco e, infine, blocco di codice) e Altri 3 sono quelli che coprono tutti i blocchi catch contemporaneamente (eseguendo il blocco try, uno del blocco catch e infine il blocco). Il problema è che il plugin Emlip di Eclipse ha mostrato che non ho coperto definitivamente il blocco. Qualche idea perché può accadere?

risposta

1

Mentre stavo testando alcuni casi, ho scoperto che probabilmente non hai coperto il caso, quando viene lanciata un'eccezione non rilevata.

Dato il seguente esempio:

import java.text.ParseException; 
import java.text.SimpleDateFormat; 

import org.junit.Test; 

public class CodeCoverageFinallyTest { 
    @Test 
    public void testMyMethod() { 
     myMethod("2015-08-31"); 
     myMethod("wrongFormat"); 
    } 

    private void myMethod(final String source) { 
     try { 
      new SimpleDateFormat("yyyy-MM-dd").parse(source); 
     } catch (final ParseException e) { 
      System.out.println("catch ParseException"); 
     } finally { 
      System.out.println("finally"); 
     } 
    } 
} 

Questo esempio servirà solo a prendere uno dei due rami nel blocco finally perché non si prova il caso, se un'eccezione unchecked (cioè un NullPointerException) sarà gettato.

Quindi, se si cambia TestCase un po ', si prenderà tutte le filiali nel blocco finally:

public void testMyMethod() { 
    myMethod("2015-08-31"); 
    myMethod("wrongFormat"); 
    myMethod(null); // also cover the case, that an unchecked and unhandled exception 
         // will be thrown 
} 

in altri miei TestCase ho avuto un caso sligthly diverso con qualche if-else-if costrutto.

import org.junit.Test; 

public class CodeCoverageIfElseTest { 
    @Test 
    public void testMyMethod() { 
     myMethod("2015-08-31"); 
     myMethod("wrongFormat"); 
    } 

    private void myMethod(final String source) { 
     if ("2015-08-31".equals(source)) { 
      System.out.println("Correct format"); 
     } else if ("wrongFormat".equals(source)) { 
      System.out.println("Incorrect format"); 
     } 
    } 
} 

Qui il else if non prendere il secondo ramo perché, che cosa succede se la condizione ifeelse if non sarà vero? Verrà rilevato anche se fornisci altri valori rispetto a entrambi nella dichiarazione if-else-if.

5

Nel bytecode Java (almeno da Java 1.6) non esiste un costrutto speciale per il blocco finally, quindi viene duplicato più volte. Ad esempio, si consideri il seguente metodo:

public static void main(String[] args) { 
    try { 
     System.out.println("In try"); 
     if(args.length > 0) 
      return; 
     System.out.println("No args"); 
    } 
    catch(RuntimeException ex) { 
     System.out.println("In catch"); 
    } 
    finally { 
     System.out.println("In finally"); 
    } 
} 

Questo codice è effettivamente compilato a qualcosa di simile:

public static void main(String[] args) { 
    try { 
     System.out.println("In try"); 
     if(args.length > 0) { 
      System.out.println("In finally"); 
      return; 
     } 
     System.out.println("No args"); 
    } 
    catch(RuntimeException ex) { 
     System.out.println("In catch"); 
     System.out.println("In finally"); 
    } 
    catch(<any exception> t) { 
     System.out.println("In finally"); 
     throw t; 
    } 
    System.out.println("In finally"); 
} 

Questo non è un codice del tutto equivalente, perché se nuova eccezione si verifica durante il System.out.println("In finally"); (prima il ritorno), quindi non verrà catturato. Tuttavia mostra l'idea approssimativa che il blocco finally è duplicato qui quattro volte. Può essere duplicato molte più volte se hai diversi modi per uscire dal tuo blocco try e specialmente se hai blocchi try-finally nidificati. Si noti inoltre la cattura speciale <any exception> aggiunta. Verrà visualizzato nel bytecode anche se si scrive esplicitamente catch(Throwable t).

Poiché gli strumenti di copertura del codice come Emma o JaCoCo funzionano a livello di codice byte, non sono consapevoli del fatto che questi quattro numeri di stampa "In finally" corrispondono effettivamente al codice sorgente. È possibile eseguire un'analisi bytecode e determinare in modo abbastanza affidabile quali parti del bytecode corrispondano al blocco di una singola sorgente (ho effettivamente scritto un analizzatore una volta), ma non è un problema molto facile e presenta alcuni avvertimenti non banali. Dovresti anche considerare che diversi compilatori (ad esempio, javac ed ecj) producono un layout leggermente diverso dei blocchi finali. Sembra quindi che questo lavoro non sia stato fatto con strumenti di copertura popolari e considerino le diverse copie di blocco come codice diverso.

Nel tuo caso particolare sembra che @bobbel abbia ragione: non hai verificato il caso di eccezione non rilevata (che cattura <any exception>).

+0

"Da notare anche la cattura speciale aggiunto apparirà nel bytecode anche se scrivi esplicitamente catch (Lancio a mano). " - Credo che questo sia sbagliato, cioè 'catch (java/lang/Throwable)' e 'catch (any)' - due distinti gestori di eccezioni in bytecode, anche se 'Throwable' è una classe base di tutti gli errori e le eccezioni. – Godin

0

Screenshot about green coverage

Sì, il ramo mancante, quando un throwable non rilevata gettato.

Se siete curiosi su questo argomento, vi consiglio la mia pagina GitHub, dove cerco tutte queste cose:. https://github.com/bachoreczm/basicjava