2015-08-28 15 views
10

Ich habe den folgenden Code Konstruktion:Codeabdeckung schließlich blockieren

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

I Unit-Tests hat geschrieben: die erste, die Situation abgedeckt, wenn eine Ausnahme nicht ausgelöst wird (Ausführung nur Blockcode versuchen und schließlich Code-Block) und 3 andere sind, welche von ihnen ist jeder catch-Block auf einmal (Ausführen von try-Block, einer der Catch-Block und schließlich blockieren). Problem ist, dass das Eclipse-Emma-Plugin gezeigt hat, dass ich nicht endgültig geblockt habe. Irgendwelche Ideen, warum kann es passieren?

Antwort

1

Während ich einige Fälle testete, fand ich heraus, dass Sie den Fall wahrscheinlich nicht abgedeckt haben, wenn eine nicht abgeholte Exception geworfen wird.

das folgende Beispiel Gegeben:

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"); 
     } 
    } 
} 

Dieses Beispiel nur fangen wird einer der beiden Zweige in blockieren die schließlich, weil Sie den Fall nicht prüfen, ob eine ungeprüfte Ausnahme (dh eine Nullpointer) sein wird, geworfen.

Also, wenn Sie Ihren Testfall ein wenig ändern, werden Sie alle Zweige im finally-Block fangen:

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 meinem anderen Testfall hatte ich einen sligthly anderen Fall mit einigem if-else-if Konstrukt.

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"); 
     } 
    } 
} 

Hier ist der else if nicht den zweiten Zweig fangen, weil, was passiert, wenn die ifundelse if Bedingung nicht wahr sein? Es wird auch abgefangen, wenn Sie andere Werte als die beiden in der if-else-if-Anweisung angeben.

5

Im Java-Bytecode (mindestens seit Java 1.6) gibt es kein spezielles Konstrukt für den finally Block, so dass es tatsächlich viele Male dupliziert wird. Betrachten wir zum Beispiel die folgende Methode:

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"); 
    } 
} 

Dieser Code effektiv zu so etwas wie dies zusammengestellt:

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"); 
} 

Dies ist kein völlig gleichwertig Code, denn wenn neue Ausnahme während der System.out.println("In finally"); auftritt (vor die Rückkehr), dann wird es nicht eingefangen. Es zeigt jedoch die grobe Idee, dass der finally-Block hier vierfach dupliziert ist. Es kann viel öfter dupliziert werden, wenn Sie mehrere Möglichkeiten haben, Ihren try-Block zu verlassen und besonders, wenn Sie try-finally-Blöcke verschachtelt haben. Beachten Sie auch den <any exception> speziellen Fang hinzugefügt. Es erscheint auch dann im Bytecode, wenn Sie explizit catch(Throwable t) schreiben.

Da Code-Coverage-Tools wie Emma oder JaCoCo auf Byte-Code-Ebene arbeiten, sind sie sich nicht bewusst, dass diese vier "In finally" printlns tatsächlich die gleiche Anweisung im Quellcode sind. Es ist möglich, eine Bytecode-Analyse durchzuführen und ziemlich robust zu bestimmen, welche Teile von Bytecode dem Single-Source-Endblock entsprechen (ich habe tatsächlich einmal einen solchen Analysator geschrieben), aber es ist kein sehr einfaches Problem und hat einige nicht triviale Vorbehalte. Sie sollten auch berücksichtigen, dass verschiedene Compiler (zum Beispiel javac und ecj) ein etwas anderes Layout von finally-Blöcken erzeugen. So scheint es, dass diese Arbeit nicht in populären Erfassungswerkzeugen gemacht wurde und sie nur unterschiedliche Blockkopien als unterschiedlichen Code betrachten.

In Ihrem speziellen Fall scheint, dass @bobbel Recht hat: Sie haben den uncatched Ausnahmefall nicht getestet (dass <any exception> fangen).

+0

„Beachten Sie auch die spezielle Fang hinzugefügt Es wird in der Bytecode erscheinen auch wenn du explizit catch schreibst (Throwable t). " - Ich glaube, das ist falsch, d. H. 'Catch (java/lang/Throwable)' und 'catch (any)' - zwei unterschiedliche Ausnahme-Handler in Bytecode, selbst wenn 'Throwable' eine Basisklasse aller Fehler und Ausnahmen ist. – Godin

0

Screenshot about green coverage

Ja, die fehlende Zweig, wenn ein nicht erfasster throwable geworfen.

Wenn Sie zu diesem Thema neugierig sind, dann ich Ihnen meine Github Seite beraten, wo ich all diese Dinge ausprobieren. https://github.com/bachoreczm/basicjava